Blucon finished

This commit is contained in:
Johan Degraeve 2019-07-22 22:50:56 +02:00
parent b874a0bb0d
commit b1aacda83d
12 changed files with 571 additions and 94 deletions

View File

@ -100,9 +100,13 @@ extension UserDefaults {
/// timestamp of last bgreading that was stored in healthkit
case timeStampLatestHealthKitStoreBgReading = "timeStampLatestHealthKitStoreBgReading"
// Dexcom Share
/// timestamp of latest reading uploaded to Dexcom Share
case timeStampLatestDexcomShareUploadedBgReading = "timeStampLatestDexcomShareUploadedBgReading"
// Sensor
/// sensor Serial Number, for now only applicable to Libre
case sensorSerialNumber = "sensorSerialNumber"
}
// MARK: - ===== User Configurable Settings ======
@ -527,6 +531,15 @@ extension UserDefaults {
}
}
/// sensor serial number, for now only useful for Libre sensor
var sensorSerialNumber:String? {
get {
return string(forKey: Key.sensorSerialNumber.rawValue)
}
set {
set(newValue, forKey: Key.sensorSerialNumber.rawValue)
}
}
}

View File

@ -116,7 +116,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
if let level = result.batteryLevel {
transmitterBatteryInfo = TransmitterBatteryInfo.DexcomG4(level: level)
}
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
case .beaconPacket?:
os_log(" in peripheral didUpdateValueFor, received beaconPacket", log: log, type: .info)
@ -149,7 +149,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
if let batteryLevel = result.batteryLevel {
transmitterBatteryInfo = TransmitterBatteryInfo.DexcomG4(level: batteryLevel)
}
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
}
}

View File

@ -165,7 +165,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
let testdata = RawGlucoseData(timeStamp: Date(), glucoseLevelRaw: testAmount, glucoseLevelFiltered: testAmount)
debuglogging("timestamp testdata = " + testdata.timeStamp.description + ", with amount = " + testAmount.description)
var testdataasarray = [testdata]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
testAmount = testAmount + 1
}
@ -437,7 +437,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
timeStampOfLastG5Reading = Date()
let glucoseData = RawGlucoseData(timeStamp: sensorDataRxMessage.timestamp, glucoseLevelRaw: sensorDataRxMessage.unfiltered * scalingFactor, glucoseLevelFiltered: sensorDataRxMessage.filtered * scalingFactor)
var glucoseDataArray = [glucoseData]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
}
@ -538,7 +538,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
private func processBatteryStatusRxMessage(value:Data) {
if let batteryStatusRxMessage = BatteryStatusRxMessage(data: value) {
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
} else {
os_log("batteryStatusRxMessage is nil", log: log, type: .error)
}
@ -546,7 +546,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
private func processTransmitterVersionRxMessage(value:Data) {
if let transmitterVersionRxMessage = TransmitterVersionRxMessage(data: value) {
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: transmitterVersionRxMessage.firmwareVersion.hexEncodedString(), hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: transmitterVersionRxMessage.firmwareVersion.hexEncodedString(), hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
// assign transmitterVersion
transmitterVersion = transmitterVersionRxMessage.firmwareVersion.hexEncodedString()
} else {

View File

@ -90,6 +90,8 @@ enum CGMTransmitterType:String, CaseIterable {
/// if true, then a class conforming to the protocol CGMTransmitterDelegate will call newSensorDetected if it detects a new sensor is placed. Means there's no need to let the user start and stop a sensor
///
/// example MiaoMiao can detect new sensor, implementation should return true, Dexcom transmitter's can't
///
/// if true, then transmitterType must also be able to give the sensor age, ie sensorTimeInMinutes
func canDetectNewSensor() -> Bool {
switch self {
@ -107,7 +109,7 @@ enum CGMTransmitterType:String, CaseIterable {
return false
case .Blucon:
return false
return true
}
}

View File

@ -29,9 +29,10 @@ protocol CGMTransmitterDelegate:AnyObject {
/// - sensorTimeInMinutes : sensor age in minutes, only if transmitter can give that info, eg MiaoMiao, otherwise nil
/// - firmware : only if transmitter can give that info, eg G5, otherwise nil
/// - hardware : only if transmitter can give that info, eg G5, otherwise nil
/// - serialNumber : only if transmitter can give that info, eg G5, otherwise nil
/// - serialNumber : transmitter serial number, only if transmitter can give that info, eg G5, otherwise nil
/// - bootloader : for the moment only used by GNSentry, otherwise nil
func cgmTransmitterInfoReceived(glucoseData:inout [RawGlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorState:LibreSensorState?, sensorTimeInMinutes:Int?, firmware:String?, hardware:String?, serialNumber:String?, bootloader:String?)
/// - sensorSerialNumber : serial number of the sensor, only applicable for Libre transmitters (MiaoMiao, Blucon, ...)
func cgmTransmitterInfoReceived(glucoseData:inout [RawGlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorState:LibreSensorState?, sensorTimeInMinutes:Int?, firmware:String?, hardware:String?, hardwareSerialNumber:String?, bootloader:String?, sensorSerialNumber:String?)
/// transmitter needs bluetooth pairing
func cgmTransmitterNeedsPairing()

View File

@ -2,7 +2,9 @@ import Foundation
enum BluconTransmitterOpCode: String, CaseIterable {
case wakeUp = "cb010000"
case wakeUpRequest = "cb010000"
case wakeUpResponse = "810a00"
case getPatchInfoRequest = "010d0900"
@ -12,6 +14,32 @@ enum BluconTransmitterOpCode: String, CaseIterable {
case sensorNotDetected = "8b1a02000f"
case sleep = "010c0e00"
case bluconAckResponse = "8b0a00"
case unknown1Command = "010d0b00"
case unknown1CommandResponse = "8bdb"
case unknown2Command = "010d0a00"
case unknown2CommandResponse = "8bda"
case getHistoricDataAllBlocksCommand = "010d0f02002b"
case multipleBlockResponseIndex = "8bdf"
case getNowDataIndex = "010d0e0103"
case singleBlockInfoResponsePrefix = "8bde"
case singleBlockInfoPrefix = "010d0e010"
case bluconBatteryLowIndication1 = "cb020000"
case bluconBatteryLowIndication2 = "cbdb0000"
/// iterates through all cases, and as soon as one is found that starts with valueReceived, then initializes with that case. If none found then returns nil
public init?(withOpCodeValue: String) {
for opCode in BluconTransmitterOpCode.allCases {
@ -24,14 +52,16 @@ enum BluconTransmitterOpCode: String, CaseIterable {
return nil
}
public var description:String {
switch self {
case .wakeUp:
case .wakeUpRequest:
return "wakeUp"
case .wakeUpResponse:
return "wakeUpResponse"
case .getPatchInfoRequest:
return "getPatchInfo"
@ -43,6 +73,46 @@ enum BluconTransmitterOpCode: String, CaseIterable {
case .getPatchInfoResponse:
return "getPatchInfoResponse"
case .sleep:
return "sleep"
case .bluconAckResponse:
return "bluconAckResponse"
case .unknown1Command:
return "unknown1Command"
case .unknown1CommandResponse:
return "unknown1CommandResponse"
case .unknown2Command:
return "unknown2Command"
case .unknown2CommandResponse:
return "unknown2CommandResponse"
case .getHistoricDataAllBlocksCommand:
return "getHistoricDataAllBlocksCommand"
case .multipleBlockResponseIndex:
return "multipleBlockResponseIndex"
case .getNowDataIndex:
return "getNowDataIndex"
case .singleBlockInfoResponsePrefix:
return "singleBlockInfoResponsePrefixResponse"
case .singleBlockInfoPrefix:
return "singleBlockInfoPrefix"
case .bluconBatteryLowIndication1:
return "bluconBatteryLowIndication1"
case .bluconBatteryLowIndication2:
return "bluconBatteryLowIndication2"
}
}
}

View File

@ -1,39 +1,46 @@
import Foundation
fileprivate let lookupTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "T", "U", "V", "W", "X", "Y", "Z"]
/// static functions for Blucon
class BluconUtilities {
/*public static func decodeSerialNumber(input: Data) -> String{
let uuidShort = Data(bytes: <#T##Sequence#>)//new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
int i;
for (i = 2; i < 8; i++) uuidShort[i - 2] = input[(2 + 8) - i];
uuidShort[6] = 0x00;
uuidShort[7] = 0x00;
String binary = "";
String binS = "";
for (i = 0; i < 8; i++) {
binS = String.format("%8s", Integer.toBinaryString(uuidShort[i] & 0xFF)).replace(' ', '0');
binary += binS;
}
String v = "0";
char[] pozS = {0, 0, 0, 0, 0};
for (i = 0; i < 10; i++) {
for (int k = 0; k < 5; k++) pozS[k] = binary.charAt((5 * i) + k);
int value = (pozS[0] - '0') * 16 + (pozS[1] - '0') * 8 + (pozS[2] - '0') * 4 + (pozS[3] - '0') * 2 + (pozS[4] - '0') * 1;
v += lookupTable[value];
}
Log.e(TAG, "decodeSerialNumber=" + v);
return v;
}*/
/// - parameters:
/// - input : data received from Blucon
/// - returns: The sensor serial number
///
/// decodes serial number, copied forp xdripplus , commit 2b25bfdf6a563aea16de63053aec5e0e3be16e5f
public static func decodeSerialNumber(input: Data) -> String {
var uuidShort = Data(bytes: [0, 0, 0, 0, 0, 0, 0, 0])
for i in 2..<8 {
uuidShort[i - 2] = input[(2 + 8) - i]
}
uuidShort[6] = 0x00
uuidShort[7] = 0x00
var binary = ""
for i in 0..<8 {
var binS = String(uuidShort[i] & 0xFF, radix: 2)
while binS.count < 8 {
binS = "0" + binS
}
binary = binary + binS
}
var v = "0"
var pozS = [0, 0, 0, 0, 0]
for i in 0..<10 {
for k in 0..<5 {
let index = (5 * i) + k
pozS[k] = binary[index..<(index + 1)] == "0" ? 0:1
}
let value = pozS[0] * 16 + pozS[1] * 8 + pozS[2] * 4 + pozS[3] * 2 + pozS[4] * 1
v += lookupTable[value]
}
return v;
}
}
fileprivate let lookupTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"A", "C", "D", "E", "F", "G", "H", "J", "K", "L",
"M", "N", "P", "Q", "R", "T", "U", "V", "W", "X",
"Y", "Z"]

View File

@ -10,13 +10,16 @@ class CGMBluconTransmitter: BluetoothTransmitter {
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
/// Blucon Service
let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
private let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
/// receive characteristic
let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
private let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
/// write characteristic
let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
private let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
/// if value starts with this string, then it's assume that a battery low indication is sent by the Blucon
private let unknownCommand2BatteryLowIndicator = "8bda02"
/// for OS_log
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryBlucon)
@ -30,12 +33,47 @@ class CGMBluconTransmitter: BluetoothTransmitter {
// waiting successful pairing yes or not
private var waitingSuccessfulPairing:Bool = false
// current sensor serial number, if nil then it's not known yet
private var sensorSerialNumber:String?
/// used as parameter in call to cgmTransmitterDelegate.cgmTransmitterInfoReceived, when there's no glucosedata to send
private var emptyArray: [RawGlucoseData] = []
/// timestamp when wakeUpResponse was sent to Blucon
private var timeStampLastWakeUpResponse:Date?
/// BluconACKResponse will come in two different situations
/// - after we have sent an ackwakeup command
/// - after we have a sleep command
/// shouldSendUnknown1CommandAfterReceivingWakeUpResponse and timeStampLastWakeUpResponsetime work together to determine the case
private var shouldSendUnknown1CommandAfterReceivingBluconAckResponse = true
/// used when processing Blucon data packet
private var timestampFirstPacketReception:Date?
// how long to wait for next packet before considering the session as failed
private let maxWaitForHistoricDataInSeconds = 5.0
// receive buffer for Blucon packets
private var rxBuffer:Data
// used in Blucon protocol
private var nowGlucoseOffset:UInt8 = 0
// used in Blucon protocol - corresponds to m_getNowGlucoseDataCommand
private var waitingForGlucoseData = false
// timestamp of sending singleBlockInfoPrefix command, if time of receiving singleBlockInfoResponsePrefix is too late, then reading will be ignored
private var timeStampOfSendingSingleBlockInfoPrefix:Date?
// MARK: - public functions
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
/// - delegate : CGMTransmitterDelegate
/// - sensorSerialNumber : is needed to allow detection of a new sensor.
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
// assign addressname and name or expected devicename
// start by using expected device name
@ -46,8 +84,14 @@ class CGMBluconTransmitter: BluetoothTransmitter {
actualDeviceAddress = address
}
//initialize timeStampLastBgReading
// initialize timeStampLastBgReading
self.timeStampLastBgReading = timeStampLastBgReading
// initialize sensorSerialNumber
self.sensorSerialNumber = sensorSerialNumber
// initialize rxbuffer
rxBuffer = Data()
// initialize
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_BluconService)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Blucon, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Blucon, startScanningAfterInit: CGMTransmitterType.Blucon.startScanningAfterInit())
@ -71,14 +115,124 @@ class CGMBluconTransmitter: BluetoothTransmitter {
if !returnValue.uppercased().startsWith("BLU") {
while returnValue.count < 5 {
returnValue = "0" + returnValue;
returnValue = "0" + returnValue
}
returnValue = "BLU" + returnValue;
returnValue = "BLU" + returnValue
}
return returnValue
}
/// writes command to blucon, withResponse, and also logs the command
private func sendCommandToBlucon(opcode:BluconTransmitterOpCode) {
os_log(" send opcode %{public}@ to Blucon", log: log, type: .info, opcode.description)
_ = writeDataToPeripheral(data: Data(hexadecimalString: opcode.rawValue)!, type: .withResponse)
}
/// reset rxBuffer, reset timestampFirstPacketReception, stop packetRxMonitorTimer
private func resetRxBuffer() {
rxBuffer = Data()
timestampFirstPacketReception = Date()
}
/// process new historic data block received from Blucon, one block is the contents when receiving multipleBlockResponseIndex, inclusive the opcode - this is used if we ask all Libre data from the transmitter, which includes sensorTime and sensorStatus
/// - returns:
/// - did receive all data yes or no, if yes, then blucon can go to sleep
/// also calls delegate with result of new readings
private func handleNewHistoricData(block: Data) -> Bool {
//check if buffer needs to be reset
if let timestampFirstPacketReception = timestampFirstPacketReception {
if (Date() > timestampFirstPacketReception.addingTimeInterval(maxWaitForHistoricDataInSeconds - 1)) {
os_log("in handleNewHistoricData, more than %{public}d seconds since last update - or first update since app launch, resetting buffer", log: log, type: .info,maxWaitForHistoricDataInSeconds)
resetRxBuffer()
}
}
//add new packet to buffer, ignoring the opcode (2 bytes), the number of the next block (1 byte), and the number of blocks in the data (1 byte)
rxBuffer.append(block[4..<block.count])
// if rxBuffer has reached minimum lenght, then start processing
if rxBuffer.count >= 344 {
os_log("in handleNewHistoricData, reached minimum length, processing data", log: log, type: .info)
// crc check
guard Crc.LibreCrc(data: &rxBuffer, headerOffset: 0) else {
os_log(" crc check failed, no further processing", log: log, type: .error)
// transmitter can go to sleep
return true
}
//get readings from buffer and send to delegate
var result = parseLibreData(data: &rxBuffer, timeStampLastBgReadingStoredInDatabase: timeStampLastBgReading, headerOffset: 0)
//TODO: sort glucosedata before calling newReadingsReceived
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: nil, sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
//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 {
timeStampLastBgReading = result.glucoseData[0].timeStamp
}
//reset the buffer
resetRxBuffer()
// transmitter can go to sleep
return true
}
// transmitter should send more data
return false
}
private func blockNumberForNowGlucoseData(input:Data) -> String {
// caculate byte position in sensor body, decrement index to get the index where the last valid BG reading is stored
var nowGlucoseIndex2 = input[5] * 6 + 4 - 6
// adjust round robin
if nowGlucoseIndex2 < 4 {
nowGlucoseIndex2 = nowGlucoseIndex2 + 96
}
// calculate the absolute block number which correspond to trend index
let nowGlucoseIndex3 = 3 + (nowGlucoseIndex2/8)
// calculate offset of the 2 bytes in the block
nowGlucoseOffset = nowGlucoseIndex2 % 8
let nowGlucoseDataAsHexString = nowGlucoseIndex3.description
return nowGlucoseDataAsHexString
}
private func nowGetGlucoseValue(input:Data) -> Double {
// example 8BDE07DB010F04C868DB01
// value 1 = 0F
// value 2 = 04
//rawGlucose = (input[3 + nowGlucoseOffset + 1] & 0x0F) * 256 + input[3 + nowGlucoseOffset] = 1039
let value1 = input[3 + Int(nowGlucoseOffset)]
let value2 = input[3 + Int(nowGlucoseOffset) + 1]
let rawGlucose = Double((UInt16(value2 & 0x0F)<<8) | UInt16(value1 & 0xFF))
// rescale for Libre
let curGluc = rawGlucose * Constants.BloodGlucose.libreMultiplier
return(curGluc)
}
}
extension CGMBluconTransmitter: CGMTransmitter {
@ -164,29 +318,220 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
// get Opcode
if let opCode = BluconTransmitterOpCode(withOpCodeValue: valueAsString) {
os_log(" opcode = %{public}@", log: log, type: .info, opCode.description)
os_log(" received opcode = %{public}@ from Blucon", log: log, type: .info, opCode.description)
switch opCode {
case .wakeUp:
// send getPatchInfo command
_ = writeDataToPeripheral(data: Data(hexadecimalString: BluconTransmitterOpCode.getPatchInfoRequest.rawValue)!, type: .withResponse)
case .getPatchInfoRequest:
// shouldn't receive this ?
return
case .getPatchInfoRequest, .wakeUpResponse, .sleep, .unknown1Command, .unknown2Command, .getHistoricDataAllBlocksCommand, .getNowDataIndex, .singleBlockInfoPrefix:
// these are commands that app sends to Blucon, shouldn't receive any of them
break
case .wakeUpRequest:
// start by setting waitingForGlucoseData to false, it might still have value true due to protocol error
waitingForGlucoseData = false
// send getPatchInfoRequest
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getPatchInfoRequest)
// by default set battery level to 100
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 100), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
case .error14:
// Blucon didn't receive the next command it was waiting for, need to wait 5 minutes
os_log(" Timeout received, need to wait 5 minutes or push button to restart!", log: log, type: .error)
// and send Blucon to sleep
sendCommandToBlucon(opcode: .sleep)
case .sensorNotDetected:
// Blucon didn't detected sensor, call delegate
// Blucon didn't detect sensor, call delegate
cgmTransmitterDelegate?.sensorNotDetected()
// and send Blucon to sleep
sendCommandToBlucon(opcode: .sleep)
case .getPatchInfoResponse:
// get serial number
let newSerialNumber = BluconUtilities.decodeSerialNumber(input: value)
// verify serial number and if changed inform delegate
if newSerialNumber != sensorSerialNumber {
os_log(" new sensor detected : %{public}@", log: log, type: .info, newSerialNumber)
sensorSerialNumber = newSerialNumber
// inform delegate about new sensor detected
cgmTransmitterDelegate?.newSensorDetected()
// also reset timestamp last reading, to be sure that if new sensor is started, we get historic data
timeStampLastBgReading = Date(timeIntervalSince1970: 0)
}
// read sensorState
let sensorState = LibreSensorState(stateByte: value[17])
// if sensor is ready then send Ack, otherwise send sleep
if sensorState == LibreSensorState.ready {
timeStampLastWakeUpResponse = Date()
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = true
sendCommandToBlucon(opcode: BluconTransmitterOpCode.wakeUpResponse)
} else {
os_log(" sensorState = %{public}@", log: log, type: .info, sensorState.description)
sendCommandToBlucon(opcode: BluconTransmitterOpCode.sleep)
}
// inform delegate about sensorSerialNumber and sensorState
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: sensorState, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: sensorSerialNumber)
return
case .bluconAckResponse:
// BluconACKResponse will come in two different situations
// 1) after we have sent an ackwakeup command ==> need to send unknown1Command
// 2) after we have sent a sleep command ==> no command to send
// to verify in which case we are, timeStampLastWakeUpResponse is used
// assuming bluconAckResponse will arrive less than 5 seconds after having send wakeUpResponse
if let timeStampLastWakeUpResponse = timeStampLastWakeUpResponse, abs(timeStampLastWakeUpResponse.timeIntervalSinceNow) < 5 && shouldSendUnknown1CommandAfterReceivingBluconAckResponse {
// set to false to be sure
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
// send unknown1Command
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown1Command)
} else {
os_log(" no further processing, Blucon is sleeping now and should send a new reading in 5 minutes", log: log, type: .info)
}
case .unknown1CommandResponse:
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown2Command)
case .unknown2CommandResponse:
// check if there's a battery low indication
if valueAsString.startsWith(unknownCommand2BatteryLowIndicator) {
// this is considered as battery level 5%
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 5), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
// if timeStampLastBgReading > 5 minutes ago, then we'll get historic data, otherwise just get the latest reading
if abs(timeStampLastBgReading.timeIntervalSinceNow) > 5 * 60 + 10 {
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getHistoricDataAllBlocksCommand)
} else {
// not asking for sensorAge as in Spike and xdripplus, we know the sensorAge because we started with getHistoricDataAllBlocksCommand
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getNowDataIndex)
}
case .multipleBlockResponseIndex:
if handleNewHistoricData(block: value) {
// when Blucon responds with bluconAckResponse, then there's no need to send unknown1Command
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
// send sleep command
sendCommandToBlucon(opcode: .sleep)
}
case .singleBlockInfoResponsePrefix:
if !waitingForGlucoseData {
// get blockNumber and compose command
let commandToSend = BluconTransmitterOpCode.singleBlockInfoPrefix.rawValue + blockNumberForNowGlucoseData(input: value)
// convert command to hexstring, might fail if blockNumberForNowGlucoseData returned an invalid value
if let commandToSendAsData = Data(hexadecimalString: commandToSend) {
os_log(" send %{public}@ to Blucon", log: log, type: .info, commandToSend)
_ = writeDataToPeripheral(data: commandToSendAsData, type: .withResponse)
waitingForGlucoseData = true
} else {
os_log(" failed to convert commandToSend to Data", log: log, type: .error)
}
} else {
// reset waitingForGlucoseData to false as we will not wait for glucosedata, after having processed this reading
waitingForGlucoseData = false
// to be sure that waitingForGlucoseData is not having value true due to having broken protcol, verify when SingleBlockInfoPrefix was sent
if let timeStampOfSendingSingleBlockInfoPrefix = timeStampOfSendingSingleBlockInfoPrefix {
// should be a matter of milliseconds, so take 2 seconds
if abs(timeStampOfSendingSingleBlockInfoPrefix.timeIntervalSinceNow) > 2 {
os_log(" time since sending SingleBlockInfoPrefix is more than 2 seconds, ignoring this reading", log: log, type: .error)
// send sleep command
sendCommandToBlucon(opcode: .sleep)
return
}
}
// checking now timestamp of last reading, if less than 30 seconds old, then reading will be ignored, seems a bit late to do that check, but after a few tests it seems to be the best to continue up to here to make sure the Blucon stays in a consistent state.
if abs(timeStampLastBgReading.timeIntervalSinceNow) < 30 {
os_log(" last reading less than 30 seconds old, ignoring this one", log: log, type: .info)
} else {
os_log(" creating glucoseValue", log: log, type: .info)
// create glucose reading with timestamp now
timeStampLastBgReading = Date()
// get glucoseValue from value
let glucoseValue = nowGetGlucoseValue(input: value)
let glucoseData = RawGlucoseData(timeStamp: timeStampLastBgReading, glucoseLevelRaw: glucoseValue, glucoseLevelFiltered: glucoseValue)
var glucoseDataArray = [glucoseData]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
sendCommandToBlucon(opcode: .sleep)
}
case .bluconBatteryLowIndication1:
// this is considered as battery level 3%
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 3), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
case .bluconBatteryLowIndication2:
// this is considered as battery level 2%
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 2), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
}
} else {

View File

@ -154,22 +154,22 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
actualSerialNumber = String(data: value, encoding: String.Encoding.utf8)
// TODO : is this the serial number of the sensor ? if yes we can use this to detect new sensor ? as with blucon ?
if let actualSerialNumber = actualSerialNumber {
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: actualSerialNumber, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: actualSerialNumber, bootloader: nil, sensorSerialNumber: nil)
}
case .CBUUID_Firmware:
actualFirmWareVersion = String(data: value, encoding: String.Encoding.utf8)
if let actualFirmWareVersion = actualFirmWareVersion {
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: actualFirmWareVersion, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: actualFirmWareVersion, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
case .CBUUID_Bootloader:
actualBootLoader = String(data: value, encoding: String.Encoding.utf8)
if let actualBootLoader = actualBootLoader {
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: actualBootLoader)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: actualBootLoader, sensorSerialNumber: nil)
}
case .CBUUID_BatteryLevel:
let dataAsString = value.hexEncodedString()
if let batteryLevel = Int(dataAsString, radix: 16) {
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryLevel), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryLevel), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
} else {
os_log(" in peripheralDidUpdateValueFor, could not read batterylevel, received hex value = %{public}@", log: log, type: .error , dataAsString)
}
@ -241,7 +241,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
i = i + 1
}
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorState: sensorStatus, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes), firmware: actualFirmWareVersion, hardware: nil, serialNumber: actualSerialNumber, bootloader: actualBootLoader)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorState: sensorStatus, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes), firmware: actualFirmWareVersion, hardware: nil, hardwareSerialNumber: actualSerialNumber, bootloader: actualBootLoader, sensorSerialNumber: nil)
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
if readings.count > 0 {

View File

@ -31,7 +31,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
private var resendPacketCounter:Int = 0
/// used when processing MiaoMiao data packet
private var startDate:Date
private var timestampFirstPacketReception:Date
// receive buffer for miaomiao packets
private var rxBuffer:Data
// how long to wait for next packet before sending startreadingcommand
@ -56,7 +56,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// initialize rxbuffer
rxBuffer = Data()
startDate = Date()
timestampFirstPacketReception = Date()
//initialize timeStampLastBgReading
self.timeStampLastBgReading = timeStampLastBgReading
@ -106,7 +106,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
if let value = characteristic.value {
//check if buffer needs to be reset
if (Date() > startDate.addingTimeInterval(CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds - 1)) {
if (Date() > timestampFirstPacketReception.addingTimeInterval(CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds - 1)) {
os_log("in peripheral didUpdateValueFor, more than %{public}d seconds since last update - or first update since app launch, resetting buffer", log: log, type: .info, CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds)
resetRxBuffer()
}
@ -129,11 +129,11 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
let firmware = String(describing: rxBuffer[14...15].hexEncodedString())
let hardware = String(describing: rxBuffer[16...17].hexEncodedString())
let batteryPercentage = Int(rxBuffer[13])
//get readings from buffer and send to delegate
var result = parseLibreData(data: &rxBuffer, timeStampLastBgReadingStoredInDatabase: timeStampLastBgReading, headerOffset: miaoMiaoHeaderLength)
//TODO: sort glucosedata before calling newReadingsReceived
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, serialNumber: nil, bootloader: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
//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 {
@ -212,10 +212,10 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// MARK: - helpers
/// reset rxBuffer, reset startDate, stop packetRxMonitorTimer, set resendPacketCounter to 0
/// reset rxBuffer, reset startDate, set resendPacketCounter to 0
private func resetRxBuffer() {
rxBuffer = Data()
startDate = Date()
timestampFirstPacketReception = Date()
resendPacketCounter = 0
}

View File

@ -13,8 +13,8 @@ import Foundation
/// - notYetStarted: 0x01 sensor not yet started
/// - starting: 0x02 sensor is in the starting phase
/// - ready: 0x03 sensor is ready, i.e. in normal operation mode
/// - stateFour: 0x04 state with yet unknown meaning
/// - expired: 0x05 sensor is expired
/// - expired: 0x04 sensor is expired, status after 14 days, less than 14,5 days
/// - shutdown: 0x05 sensor stops operation after 15d after start
/// - failure: 0x06 sensor has an error
/// - unknown: any other state
enum LibreSensorState {

View File

@ -52,6 +52,9 @@ final class RootViewController: UIViewController {
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - initiate pairing
private let applicationManagerKeyInitiatePairing = "RootViewController-InitiatePairing"
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - initial calibration
private let applicationManagerKeyInitialCalibration = "RootViewController-InitialCalibration"
// MARK: - Properties - other private properties
/// a reference to the CGMTransmitter currently in use - nil means there's none, because user hasn't selected yet all required settings
@ -251,7 +254,11 @@ final class RootViewController: UIViewController {
})
}
private func processNewCGMInfo(glucoseData: inout [RawGlucoseData], sensorState: LibreSensorState?, firmware: String?, hardware: String?, transmitterBatteryInfo: TransmitterBatteryInfo?, sensorTimeInMinutes: Int?) {
/// process new glucose data received from transmitter.
/// - parameters:
/// - glucoseData : array with new readings
/// - 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 [RawGlucoseData], sensorTimeInMinutes: Int?) {
// check that calibrations and coredata manager is not nil
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager else {
@ -354,10 +361,6 @@ final class RootViewController: UIViewController {
}
}
// check transmitterBatteryInfo and if available store in settings
if let transmitterBatteryInfo = transmitterBatteryInfo {
UserDefaults.standard.transmitterBatteryInfo = transmitterBatteryInfo
}
}
// MARK:- observe function
@ -598,7 +601,7 @@ final class RootViewController: UIViewController {
case .Blucon:
if let currentTransmitterId = UserDefaults.standard.transmitterId {
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
calibrator = Libre1Calibrator()
}
@ -622,7 +625,7 @@ final class RootViewController: UIViewController {
}
}
// creates initial calibration request notification
/// creates initial calibration request notification
private func createInitialCalibrationRequest() {
// first remove existing notification if any
@ -649,9 +652,25 @@ final class RootViewController: UIViewController {
os_log("Unable to Add Notification Request : %{public}@", log: self.log, type: .error, error.localizedDescription)
}
}
// we will not just count on it that the user will click the notification to open the app (assuming the app is in the background, if the app is in the foreground, then we come in another flow)
// whenever app comes from-back to foreground, requestCalibration needs to be called
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyInitialCalibration, closure: {
// first of all reremove from application key manager
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitialCalibration)
// remove existing notification if any
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest])
// request the calibration
self.requestCalibration(userRequested: false)
})
}
// creates bgreading notification
/// creates bgreading notification
private func createBgReadingNotification() {
// bgReadingsAccessor should not be nil at all, but let's not create a fatal error for that, there's already enough checks for it
@ -921,16 +940,21 @@ final class RootViewController: UIViewController {
}
// stop the active sensor
// stops the active sensor and sets sensorSerialNumber in UserDefaults to nil
private func stopSensor() {
if let activeSensor = activeSensor, let coreDataManager = coreDataManager {
activeSensor.endDate = Date()
coreDataManager.saveChanges()
}
activeSensor = nil
// save the changes
coreDataManager?.saveChanges()
activeSensor = nil
// reset also serialNubmer to nil
UserDefaults.standard.sensorSerialNumber = nil
}
// start a new sensor, ask user for starttime
@ -1120,8 +1144,7 @@ extension RootViewController:CGMTransmitterDelegate {
let maxTimeUserCanOpenApp = Date(timeIntervalSinceNow: TimeInterval(Constants.DexcomG5.maxTimeToAcceptPairingInSeconds - 1))
// we will not just count on it that the user will click the notification to open the app (assuming the app is in the background, if the app is in the foreground, then we come in another flow)
// we will
// whenever app comes from-back to freground, updateLabels needs to be called
// whenever app comes from-back to foreground, updateLabels needs to be called
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyInitiatePairing, closure: {
// first of all reremove from application key manager
@ -1175,18 +1198,32 @@ extension RootViewController:CGMTransmitterDelegate {
/// - parameters:
/// - readings: first entry is the most recent
func cgmTransmitterInfoReceived(glucoseData: inout [RawGlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorState: LibreSensorState?, sensorTimeInMinutes: Int?, firmware: String?, hardware: String?, serialNumber: String?, bootloader: String?) {
func cgmTransmitterInfoReceived(glucoseData: inout [RawGlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorState: LibreSensorState?, sensorTimeInMinutes: Int?, firmware: String?, hardware: String?, hardwareSerialNumber: String?, bootloader: String?, sensorSerialNumber:String?) {
os_log("sensorstate %{public}@", log: log, type: .debug, sensorState?.description ?? "no sensor state found")
os_log("firmware %{public}@", log: log, type: .debug, firmware ?? "no firmware version found")
os_log("bootloader %{public}@", log: log, type: .debug, bootloader ?? "no bootloader found")
os_log("serialNumber %{public}@", log: log, type: .debug, serialNumber ?? "no serialNumber found")
os_log("hardwareSerialNumber %{public}@", log: log, type: .debug, hardwareSerialNumber ?? "no serialNumber found")
os_log("sensorSerialNumber %{public}@", log: log, type: .debug, sensorSerialNumber ?? "no sensorSerialNumber found")
os_log("hardware %{public}@", log: log, type: .debug, hardware ?? "no hardware version found")
os_log("transmitterBatteryInfo %{public}@", log: log, type: .debug, transmitterBatteryInfo?.description ?? 0)
os_log("sensor time in minutes %{public}@", log: log, type: .debug, sensorTimeInMinutes?.description ?? "not received")
os_log("glucoseData size = %{public}@", log: log, type: .debug, glucoseData.count.description)
// if received sensorSerialNumber not nil, and if value different from currently stored value, then store it
if let sensorSerialNumber = sensorSerialNumber {
if sensorSerialNumber != UserDefaults.standard.sensorSerialNumber {
UserDefaults.standard.sensorSerialNumber = sensorSerialNumber
}
}
processNewCGMInfo(glucoseData: &glucoseData, sensorState: sensorState, firmware: firmware, hardware: hardware, transmitterBatteryInfo: transmitterBatteryInfo, sensorTimeInMinutes: sensorTimeInMinutes)
// if received transmitterBatteryInfo not nil, then store it
if let transmitterBatteryInfo = transmitterBatteryInfo {
UserDefaults.standard.transmitterBatteryInfo = transmitterBatteryInfo
}
// process new readings
processNewGlucoseData(glucoseData: &glucoseData, sensorTimeInMinutes: sensorTimeInMinutes)
}
}
@ -1217,6 +1254,9 @@ extension RootViewController:UNUserNotificationCenterDelegate {
// request calibration
requestCalibration(userRequested: false)
/// remove applicationManagerKeyInitialCalibration from application key manager - there's no need to initiate the calibration via this closure
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitialCalibration)
// call completionhandler to avoid that notification is shown to the user
completionHandler([])
@ -1262,9 +1302,8 @@ extension RootViewController:UNUserNotificationCenterDelegate {
if response.notification.request.identifier == Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest {
os_log(" userNotificationCenter didReceive, user pressed calibration notification to open the app", log: log, type: .info)
// request calibration
requestCalibration(userRequested: false)
// nothing required, the requestCalibration function will be called as it's been added to ApplicationManager
os_log(" userNotificationCenter didReceive, user pressed calibration notification to open the app, requestCalibration should be called because closure is added in ApplicationManager.shared", log: log, type: .info)
} else if response.notification.request.identifier == Constants.Notifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected {