temp commit

This commit is contained in:
Johan Degraeve 2021-11-15 11:53:54 +01:00
parent c4001d0bd2
commit 79ec1fabd1
46 changed files with 1247 additions and 502 deletions

View File

@ -228,6 +228,7 @@
F8252867243E50FE0067AF77 /* ConstantsLibre.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8252866243E50FE0067AF77 /* ConstantsLibre.swift */; };
F825286A2443AE190067AF77 /* DexcomG6BluetoothPeripheralViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F82528692443AE190067AF77 /* DexcomG6BluetoothPeripheralViewModel.swift */; };
F825286C2443BEDC0067AF77 /* CGMG6TransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F825286B2443BEDC0067AF77 /* CGMG6TransmitterDelegate.swift */; };
F8284230274ED56A0097E0C9 /* DexcomCalibrationParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = F828422F274ED56A0097E0C9 /* DexcomCalibrationParameters.swift */; };
F8297F4E238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F4B238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift */; };
F8297F4F238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F4C238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift */; };
F8297F52238ECA3200D74D66 /* BluetoothPeripheralViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F51238ECA3200D74D66 /* BluetoothPeripheralViewController.swift */; };
@ -422,6 +423,13 @@
F8CB59C02734976D00BA199E /* DexcomTransmitterTimeTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59BF2734976D00BA199E /* DexcomTransmitterTimeTxMessage.swift */; };
F8CB59C22738206D00BA199E /* DexcomGlucoseDataTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59C12738206D00BA199E /* DexcomGlucoseDataTxMessage.swift */; };
F8CB59C42739D1CD00BA199E /* DexcomBackfillTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59C32739D1CD00BA199E /* DexcomBackfillTxMessage.swift */; };
F8CB59C6273ECFE500BA199E /* DexcomG6GlucoseDataRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59C5273ECFE500BA199E /* DexcomG6GlucoseDataRxMessage.swift */; };
F8CB59C8273EF9F800BA199E /* DexcomAlgorithmState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59C7273EF9F800BA199E /* DexcomAlgorithmState.swift */; };
F8CB59CA27405A6800BA199E /* DexcomCalibrationTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59C927405A6800BA199E /* DexcomCalibrationTxMessage.swift */; };
F8CB59CC2744471100BA199E /* DexcomSessionStartResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59CB2744471000BA199E /* DexcomSessionStartResponse.swift */; };
F8CB59CE27444D6300BA199E /* DexcomSessionStopResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59CD27444D6300BA199E /* DexcomSessionStopResponse.swift */; };
F8CB59D12745D3FE00BA199E /* DexcomG6FireflyBluetoothPeripheralViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59D02745D3FE00BA199E /* DexcomG6FireflyBluetoothPeripheralViewModel.swift */; };
F8CB59D3274D94AF00BA199E /* DexcomSessionStartTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CB59D2274D94AE00BA199E /* DexcomSessionStartTxMessage.swift */; };
F8D0587C24BCB570008C8734 /* SettingsViewHomeScreenSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8D0587B24BCB570008C8734 /* SettingsViewHomeScreenSettingsViewModel.swift */; };
F8DF765323E34F4500063910 /* DexcomG5+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF765223E34F4500063910 /* DexcomG5+CoreDataClass.swift */; };
F8DF765523E34FD500063910 /* DexcomG5+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8DF765423E34FD500063910 /* DexcomG5+CoreDataProperties.swift */; };
@ -461,7 +469,6 @@
F8F1670A2727317D001AA3D8 /* DexcomTransmitterTimeRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F167092727317C001AA3D8 /* DexcomTransmitterTimeRxMessage.swift */; };
F8F1670C27273774001AA3D8 /* GlucoseBackfillRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F1670B27273774001AA3D8 /* GlucoseBackfillRxMessage.swift */; };
F8F1670E27273EA7001AA3D8 /* GlucoseDataRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F1670D27273EA7001AA3D8 /* GlucoseDataRxMessage.swift */; };
F8F1671127274080001AA3D8 /* DexcomCalibrationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F1671027274080001AA3D8 /* DexcomCalibrationState.swift */; };
F8F1671327274557001AA3D8 /* DexcomCalibrationRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F1671227274557001AA3D8 /* DexcomCalibrationRxMessage.swift */; };
F8F16715272745A2001AA3D8 /* DexcomCalibrationResponseType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F16714272745A2001AA3D8 /* DexcomCalibrationResponseType.swift */; };
F8F1671727288B24001AA3D8 /* DexcomSessionStopRxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F1671627288B24001AA3D8 /* DexcomSessionStopRxMessage.swift */; };
@ -1026,6 +1033,7 @@
F8252866243E50FE0067AF77 /* ConstantsLibre.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstantsLibre.swift; sourceTree = "<group>"; };
F82528692443AE190067AF77 /* DexcomG6BluetoothPeripheralViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomG6BluetoothPeripheralViewModel.swift; sourceTree = "<group>"; };
F825286B2443BEDC0067AF77 /* CGMG6TransmitterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMG6TransmitterDelegate.swift; sourceTree = "<group>"; };
F828422F274ED56A0097E0C9 /* DexcomCalibrationParameters.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrationParameters.swift; sourceTree = "<group>"; };
F8297F4B238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralsViewController.swift; sourceTree = "<group>"; };
F8297F4C238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralNavigationController.swift; sourceTree = "<group>"; };
F8297F51238ECA3200D74D66 /* BluetoothPeripheralViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralViewController.swift; sourceTree = "<group>"; };
@ -1299,6 +1307,13 @@
F8CB59BF2734976D00BA199E /* DexcomTransmitterTimeTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomTransmitterTimeTxMessage.swift; sourceTree = "<group>"; };
F8CB59C12738206D00BA199E /* DexcomGlucoseDataTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomGlucoseDataTxMessage.swift; sourceTree = "<group>"; };
F8CB59C32739D1CD00BA199E /* DexcomBackfillTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomBackfillTxMessage.swift; sourceTree = "<group>"; };
F8CB59C5273ECFE500BA199E /* DexcomG6GlucoseDataRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomG6GlucoseDataRxMessage.swift; sourceTree = "<group>"; };
F8CB59C7273EF9F800BA199E /* DexcomAlgorithmState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomAlgorithmState.swift; sourceTree = "<group>"; };
F8CB59C927405A6800BA199E /* DexcomCalibrationTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrationTxMessage.swift; sourceTree = "<group>"; };
F8CB59CB2744471000BA199E /* DexcomSessionStartResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomSessionStartResponse.swift; sourceTree = "<group>"; };
F8CB59CD27444D6300BA199E /* DexcomSessionStopResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomSessionStopResponse.swift; sourceTree = "<group>"; };
F8CB59D02745D3FE00BA199E /* DexcomG6FireflyBluetoothPeripheralViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomG6FireflyBluetoothPeripheralViewModel.swift; sourceTree = "<group>"; };
F8CB59D2274D94AE00BA199E /* DexcomSessionStartTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomSessionStartTxMessage.swift; sourceTree = "<group>"; };
F8D0587B24BCB570008C8734 /* SettingsViewHomeScreenSettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewHomeScreenSettingsViewModel.swift; sourceTree = "<group>"; };
F8DF765223E34F4500063910 /* DexcomG5+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DexcomG5+CoreDataClass.swift"; sourceTree = "<group>"; };
F8DF765423E34FD500063910 /* DexcomG5+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DexcomG5+CoreDataProperties.swift"; sourceTree = "<group>"; };
@ -1344,7 +1359,6 @@
F8F167092727317C001AA3D8 /* DexcomTransmitterTimeRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomTransmitterTimeRxMessage.swift; sourceTree = "<group>"; };
F8F1670B27273774001AA3D8 /* GlucoseBackfillRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseBackfillRxMessage.swift; sourceTree = "<group>"; };
F8F1670D27273EA7001AA3D8 /* GlucoseDataRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlucoseDataRxMessage.swift; sourceTree = "<group>"; };
F8F1671027274080001AA3D8 /* DexcomCalibrationState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrationState.swift; sourceTree = "<group>"; };
F8F1671227274557001AA3D8 /* DexcomCalibrationRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrationRxMessage.swift; sourceTree = "<group>"; };
F8F16714272745A2001AA3D8 /* DexcomCalibrationResponseType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrationResponseType.swift; sourceTree = "<group>"; };
F8F1671627288B24001AA3D8 /* DexcomSessionStopRxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomSessionStopRxMessage.swift; sourceTree = "<group>"; };
@ -2446,6 +2460,14 @@
path = MiaoMiao;
sourceTree = "<group>";
};
F8CB59CF2745D3D000BA199E /* DexcomG6Firefly */ = {
isa = PBXGroup;
children = (
F8CB59D02745D3FE00BA199E /* DexcomG6FireflyBluetoothPeripheralViewModel.swift */,
);
path = DexcomG6Firefly;
sourceTree = "<group>";
};
F8DF765923E350B100063910 /* Dexcom */ = {
isa = PBXGroup;
children = (
@ -2475,6 +2497,7 @@
F8DF766A23ED9AF100063910 /* Dexcom */ = {
isa = PBXGroup;
children = (
F8CB59CF2745D3D000BA199E /* DexcomG6Firefly */,
F82528682443AE040067AF77 /* DexcomG6 */,
F816E12F2439E2A0009EE65B /* DexcomG4 */,
F8DF766B23ED9AF100063910 /* DexcomG5 */,
@ -2631,11 +2654,18 @@
F8F971C323A5915900C3F17D /* AuthRequestTxMessage.swift */,
F8F971D123A5915900C3F17D /* BatteryStatusRxMessage.swift */,
F8F971C523A5915900C3F17D /* BatteryStatusTxMessage.swift */,
F8CB59C7273EF9F800BA199E /* DexcomAlgorithmState.swift */,
F8F1671A272B3E4F001AA3D8 /* DexcomBackfillStream.swift */,
F8CB59C32739D1CD00BA199E /* DexcomBackfillTxMessage.swift */,
F8F16714272745A2001AA3D8 /* DexcomCalibrationResponseType.swift */,
F8F1671227274557001AA3D8 /* DexcomCalibrationRxMessage.swift */,
F8F1671027274080001AA3D8 /* DexcomCalibrationState.swift */,
F8CB59C927405A6800BA199E /* DexcomCalibrationTxMessage.swift */,
F8CB59C5273ECFE500BA199E /* DexcomG6GlucoseDataRxMessage.swift */,
F8CB59C12738206D00BA199E /* DexcomGlucoseDataTxMessage.swift */,
F8CB59CB2744471000BA199E /* DexcomSessionStartResponse.swift */,
F8F1671827288FC6001AA3D8 /* DexcomSessionStartRxMessage.swift */,
F8CB59D2274D94AE00BA199E /* DexcomSessionStartTxMessage.swift */,
F8CB59CD27444D6300BA199E /* DexcomSessionStopResponse.swift */,
F8F1671627288B24001AA3D8 /* DexcomSessionStopRxMessage.swift */,
F8F971C723A5915900C3F17D /* DexcomTransmitterOpCode.swift */,
F8F167092727317C001AA3D8 /* DexcomTransmitterTimeRxMessage.swift */,
@ -2652,8 +2682,7 @@
F8F971C223A5915900C3F17D /* TransmitterMessage.swift */,
F8F971CE23A5915900C3F17D /* TransmitterVersionRxMessage.swift */,
F8F971C123A5915900C3F17D /* TransmitterVersionTxMessage.swift */,
F8CB59C12738206D00BA199E /* DexcomGlucoseDataTxMessage.swift */,
F8CB59C32739D1CD00BA199E /* DexcomBackfillTxMessage.swift */,
F828422F274ED56A0097E0C9 /* DexcomCalibrationParameters.swift */,
);
path = Generic;
sourceTree = "<group>";
@ -3396,6 +3425,7 @@
F816E0F12433C31B009EE65B /* Blucon+CoreDataClass.swift in Sources */,
F8F9723323A5915900C3F17D /* M5StackBluetoothTransmitterDelegate.swift in Sources */,
F8B3A81B227DEC92004BA588 /* SensorsAccessor.swift in Sources */,
F8CB59D12745D3FE00BA199E /* DexcomG6FireflyBluetoothPeripheralViewModel.swift in Sources */,
F8F9721E23A5915900C3F17D /* LibreSensorState.swift in Sources */,
F8B3A85D22821BB6004BA588 /* Int.swift in Sources */,
F808D2CE2403292C0084B5DB /* Bubble+CoreDataProperties.swift in Sources */,
@ -3458,7 +3488,6 @@
F8F9723923A5928D00C3F17D /* M5StackBluetoothPeripheralViewModel.swift in Sources */,
F8F9722B23A5915900C3F17D /* CGMTransmitterDelegate.swift in Sources */,
F80859292364D61B00F3829D /* UserDefaults+charts.swift in Sources */,
F8F1671127274080001AA3D8 /* DexcomCalibrationState.swift in Sources */,
F8B955B7258D5E2000C06016 /* ConstantsHealthKit.swift in Sources */,
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
F80D917024F85C7A006840B5 /* Libre2+BluetoothPeripheral.swift in Sources */,
@ -3492,6 +3521,7 @@
F8F1670E27273EA7001AA3D8 /* GlucoseDataRxMessage.swift in Sources */,
F8B3A835227F08AC004BA588 /* PickerViewController.swift in Sources */,
F821CF9522ADB0D7005C1E43 /* HealthKitManager.swift in Sources */,
F8CB59CE27444D6300BA199E /* DexcomSessionStopResponse.swift in Sources */,
F8B3A81D227DEC92004BA588 /* CalibrationsAccessor.swift in Sources */,
F821CF9D22AEF483005C1E43 /* BGReadingSpeaker.swift in Sources */,
F8297F4E238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift in Sources */,
@ -3580,6 +3610,7 @@
F8252867243E50FE0067AF77 /* ConstantsLibre.swift in Sources */,
F8A1586D22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift in Sources */,
F8F9720923A5915900C3F17D /* AESCrypt.m in Sources */,
F8CB59D3274D94AF00BA199E /* DexcomSessionStartTxMessage.swift in Sources */,
F80ED2ED236F68F90005C035 /* SettingsViewM5StackGeneralSettingsViewModel.swift in Sources */,
F816E10A2437E7B8009EE65B /* CGMBlueReaderTransmitterDelegate.swift in Sources */,
F8B3A850227F26F8004BA588 /* AlertTypesSettingsViewController.swift in Sources */,
@ -3602,6 +3633,7 @@
F8BECB12235CEA9B0060DAE1 /* TimeInterval.swift in Sources */,
F8F1670C27273774001AA3D8 /* GlucoseBackfillRxMessage.swift in Sources */,
F8F9720823A5915900C3F17D /* BatteryStatusTxMessage.swift in Sources */,
F8CB59C6273ECFE500BA199E /* DexcomG6GlucoseDataRxMessage.swift in Sources */,
F81F9FFC2288C7530028C70F /* NewAlertSettingsViewController.swift in Sources */,
F80D916524F5B3DE006840B5 /* Libre2+CoreDataClass.swift in Sources */,
F898EDF2234A8A0500BFB79B /* UInt8.swift in Sources */,
@ -3620,6 +3652,7 @@
F8A2BC2E25DB0D6D001D1E78 /* BluetoothPeripheralManager+CGMDropletTransmitterDelegate.swift in Sources */,
F8C5EBE722F38F0E00563B5F /* Trace.swift in Sources */,
F8B48A9422B2A705009BCC01 /* TextsSpeakReading.swift in Sources */,
F8CB59CC2744471100BA199E /* DexcomSessionStartResponse.swift in Sources */,
47AB72F327105EF4005E7CAB /* SettingsViewHelpSettingModel.swift in Sources */,
F821CF5F229BF43A005C1E43 /* ApplicationManager.swift in Sources */,
F8B3A834227F08AC004BA588 /* PickerViewData.swift in Sources */,
@ -3676,6 +3709,7 @@
F816E0E42432A4FA009EE65B /* CGMBluconTransmitterDelegate.swift in Sources */,
F8A389CF232AE2EA0010F405 /* M5StackSettingsViewController.swift in Sources */,
F821CF5E229BF43A005C1E43 /* BgReading+NightScout.swift in Sources */,
F8CB59CA27405A6800BA199E /* DexcomCalibrationTxMessage.swift in Sources */,
F8A2BC3025DB0D6D001D1E78 /* BluetoothPeripheralManager.swift in Sources */,
F8A389EB233175A10010F405 /* SettingsViewM5StackSettingsViewModel.swift in Sources */,
F816E1242439DB63009EE65B /* DexcomG4+BluetoothPeripheral.swift in Sources */,
@ -3704,6 +3738,7 @@
F85FF3D1252F9FF9004E6FF1 /* SnoozeParameters+CoreDataClass.swift in Sources */,
F857A32A253E2D9E00951BB2 /* LibreAlgorithmThresholds.swift in Sources */,
F8A1850C25643B16000EF8A0 /* Double+Smoothable.swift in Sources */,
F8CB59C8273EF9F800BA199E /* DexcomAlgorithmState.swift in Sources */,
F890E07A247687AE008FB2EC /* URL.swift in Sources */,
F8B3A856227F28DC004BA588 /* AlertTypeSettingsViewController.swift in Sources */,
F8A1584F22ECB281007F5B5D /* SettingsViewInfoViewModel.swift in Sources */,
@ -3711,6 +3746,7 @@
F8F9720C23A5915900C3F17D /* SensorDataTxMessage.swift in Sources */,
F8DF766223E390D100063910 /* BLEPeripheral+CoreDataProperties.swift in Sources */,
F8A1585F22EDB81E007F5B5D /* ConstantsLog.swift in Sources */,
F8284230274ED56A0097E0C9 /* DexcomCalibrationParameters.swift in Sources */,
F8A1586522EDB89D007F5B5D /* ConstantsDefaultAlertTypeSettings.swift in Sources */,
F816E11624391A02009EE65B /* Droplet+BluetoothPeripheral.swift in Sources */,
F8FDFEAD260DE1B90047597D /* ConstantsUI.swift in Sources */,

View File

@ -4,6 +4,10 @@ extension DexcomG5: BluetoothPeripheral {
func bluetoothPeripheralType() -> BluetoothPeripheralType {
if isDexcomG6 {return .DexcomG6Type}
if isFirefly {return .DexcomG6FireflyType}
return .DexcomG5Type
}

View File

@ -25,6 +25,9 @@ enum BluetoothPeripheralType: String, CaseIterable {
/// DexcomG6
case DexcomG6Type = "Dexcom G6"
/// Dexcom G6 firefly
case DexcomG6FireflyType = "Dexcom G6 Firefly"
/// DexcomG5
case DexcomG5Type = "Dexcom G5"
@ -90,6 +93,9 @@ enum BluetoothPeripheralType: String, CaseIterable {
case .DexcomG6Type:
return DexcomG6BluetoothPeripheralViewModel()
case .DexcomG6FireflyType:
return DexcomG6FireflyBluetoothPeripheralViewModel()
case .Libre2Type:
return Libre2BluetoothPeripheralViewModel()
@ -128,8 +134,26 @@ enum BluetoothPeripheralType: String, CaseIterable {
case .DexcomG6Type:
// DexcomG6 is a DexcomG5 with isDexcomG6 set to true
// the cgm transmitter itself will use the transmitter id to determine that firefly logic needs to be used
// which means, a user could add a Dexcom G6 Firefly as a Dexcom G6 or even a Dexcom G5, the class CGMG5Transmitter will find out it's a firefly, based on transmitter id and handle it as a firefly
// It plays only a role in the RootViewController that a G6 Firefly is selected, because then also the user needs to add a code
let dexcomG6 = DexcomG5(address: address, name: name, alias: nil, nsManagedObjectContext: nsManagedObjectContext)
dexcomG6.isDexcomG6 = true
return dexcomG6
case .DexcomG6FireflyType:
// DexcomG6 Firefly is a DexcomG5 with isFirefly set to true
// the cgm transmitter itself will use the transmitter id to determine that firefly logic needs to be used
// which means, a user could add a Dexcom G6 Firefly as a Dexcom G6 or even a Dexcom G5, the class CGMG5Transmitter will find out it's a firefly, based on transmitter id and handle it as a firefly
// It plays only a role in the RootViewController that a G6 Firefly is selected, because then also the user needs to add a code
let dexcomG6 = DexcomG5(address: address, name: name, alias: nil, nsManagedObjectContext: nsManagedObjectContext)
dexcomG6.isFirefly = true
return dexcomG6
@ -181,7 +205,7 @@ enum BluetoothPeripheralType: String, CaseIterable {
case .M5StackType, .M5StickCType:
return .M5Stack
case .DexcomG5Type, .BubbleType, .MiaoMiaoType, .BluconType, .GNSentryType, .BlueReaderType, .DropletType, .DexcomG4Type, .DexcomG6Type, .WatlaaType, .Libre2Type, .AtomType:
case .DexcomG5Type, .BubbleType, .MiaoMiaoType, .BluconType, .GNSentryType, .BlueReaderType, .DropletType, .DexcomG4Type, .DexcomG6Type, .WatlaaType, .Libre2Type, .AtomType, .DexcomG6FireflyType:
return .CGM
}
@ -196,7 +220,7 @@ enum BluetoothPeripheralType: String, CaseIterable {
case .M5StackType, .M5StickCType, .WatlaaType, .BubbleType, .MiaoMiaoType, .GNSentryType, .BlueReaderType, .DropletType, .Libre2Type, .AtomType:
return false
case .DexcomG5Type, .BluconType, .DexcomG4Type, .DexcomG6Type:
case .DexcomG5Type, .BluconType, .DexcomG4Type, .DexcomG6Type, .DexcomG6FireflyType:
return true
}
@ -209,7 +233,7 @@ enum BluetoothPeripheralType: String, CaseIterable {
switch self {
case .DexcomG5Type, .DexcomG6Type:
case .DexcomG5Type, .DexcomG6Type, .DexcomG6FireflyType:
// length for G5 and G6 is 6
if transmitterId.count != 6 {
@ -261,7 +285,7 @@ enum BluetoothPeripheralType: String, CaseIterable {
switch self {
case .M5StackType, .M5StickCType, .WatlaaType, .DexcomG4Type, .DexcomG5Type, .DexcomG6Type, .BluconType, .BlueReaderType, .DropletType , .GNSentryType:
case .M5StackType, .M5StickCType, .WatlaaType, .DexcomG4Type, .DexcomG5Type, .DexcomG6Type, .BluconType, .BlueReaderType, .DropletType , .GNSentryType, .DexcomG6FireflyType:
return false
case .BubbleType, .MiaoMiaoType, .AtomType:
@ -280,7 +304,7 @@ enum BluetoothPeripheralType: String, CaseIterable {
switch self {
case .M5StackType, .M5StickCType, .DexcomG4Type, .DexcomG5Type, .DexcomG6Type:
case .M5StackType, .M5StickCType, .DexcomG4Type, .DexcomG5Type, .DexcomG6Type, .DexcomG6FireflyType:
return false
case .BubbleType, .MiaoMiaoType, .WatlaaType, .BluconType, .BlueReaderType, .DropletType , .GNSentryType, .AtomType:

View File

@ -110,7 +110,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, CGMTransmitter {
}
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorAge: nil)
}
@ -149,7 +149,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, CGMTransmitter {
cGMDexcomG4TransmitterDelegate?.received(batteryLevel: level, from: self)
}
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorAge: nil)
}
}
@ -157,41 +157,10 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, CGMTransmitter {
// MARK:- CGMTransmitter protocol functions
// this transmitter does not support Libre non fixed slopes
func setNonFixedSlopeEnabled(enabled: Bool) {
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
func setWebOOPSite(oopWebSite: String) {}
func setWebOOPToken(oopWebToken: String) {}
func cgmTransmitterType() -> CGMTransmitterType {
return .dexcomG4
}
func isNonFixedSlopeEnabled() -> Bool {
return false
}
func isWebOOPEnabled() -> Bool {
return false
}
func requestNewReading() {
// not supported for Dexcom G4
}
func maxSensorAgeInDays() -> Int? {
// no max sensor age for Dexcom
return nil
}
// MARK:- helper functions
private func processxBridgeDataPacket(value:Data) -> (glucoseData:GlucoseData?, batteryLevel:Int?, transmitterID:String?) {

View File

@ -3,7 +3,7 @@ import CoreBluetooth
import os
class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - public properties
/// G5 or G6 transmitter firmware version - only used internally, if nil then it was never received
@ -62,7 +62,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
}
// MARK: other
// MARK: - private properties
/// the write and control Characteristic
private var writeControlCharacteristic:CBCharacteristic?
@ -119,7 +119,26 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
/// to be used in firefly flow - backfillTxSent should be sent only once per connection setting
private var backfillTxSent = false
// MARK: - functions
/// - if true then xDrip4iOS will not send anything to the transmitter, it will only listen
/// - sending should be done by other app (eg official Dexcom app)
/// - exception could be sending calibration request or start sensor request, because if user is calibrating or starting the sensor via xDrip4iOS then it would need to be send to the transmitter by xDrip4iOS
private let letDexcomAppDoTheTransmitWork = false
/// - used to send calibration done by user via xDrip4iOS to Dexcom transmitter. For example, user may have given a calibration in the app, but it's not yet send to the transmitter.
/// - calibrationToSendToTransmitter.sentToTransmitter says if it's been sent to transmitter or not
private var calibrationToSendToTransmitter: Calibration?
/// if the user starts the sensor via xDrip4iOS, then only after having receivec a confirmation from the transmitter, then sensorStartDate will be assigned to the actual sensor start date
private var sensorStartDate: Date?
/// - used to send sensor start done by user via xDrip4iOS to Dexcom transmitter. For example, user may have started a sensor in the app, but it's not yet send to the transmitter.
/// - tuple consisitng of startDate and dexcomCalibrationParameters. If startDate is nil, then there's no start sensor waiting to be sent to the transmitter.
private var sensorStartToSendToTransmitter: (startDate: Date, dexcomCalibrationParameters: DexcomCalibrationParameters)?
/// if true, then firefly flow will be used, even if the transmitter id < 8G
private let isFireflyNoMatterTransmitterId: Bool
// MARK: - public functions
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
@ -128,8 +147,11 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
/// - bluetoothTransmitterDelegate : a NluetoothTransmitterDelegate
/// - cGMTransmitterDelegate : a CGMTransmitterDelegate
/// - cGMG5TransmitterDelegate : a CGMG5TransmitterDelegate
/// - transmitterStartDate : transmitter start date, optinoal
init(address:String?, name: String?, transmitterID:String, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate, cGMG5TransmitterDelegate: CGMG5TransmitterDelegate, cGMTransmitterDelegate:CGMTransmitterDelegate, transmitterStartDate: Date?, firmware: String?) {
/// - transmitterStartDate : transmitter start date, optional - actual transmitterStartDate is received from transmitter itself, and stored in coredata. The stored value iss given here as parameter in the initializer. Means at app start up, it's read from core data and added here as parameter
/// - sensorStartDate : should be sensorStartDate of active sensor. If a different sensor start date is received from the transmitter, then we know a new senosr was started
/// - calibrationToSendToTransmitter : used to send calibration done by user via xDrip4iOS to Dexcom transmitter. For example, user may have give a calibration in the app, but it's not yet send to the transmitter. This needs to be verified in CGMG5Transmitter, which is why it's given here as parameter - when initializing, assign last known calibration for the active sensor, even if it's already sent.
/// - isFireFly : if true then the transmitter will be treated as a firefly, no matter the transmitter id, no matter if it's a G5, G6 or real Firefly
init(address:String?, name: String?, transmitterID:String, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate, cGMG5TransmitterDelegate: CGMG5TransmitterDelegate, cGMTransmitterDelegate:CGMTransmitterDelegate, transmitterStartDate: Date?, sensorStartDate: Date?, calibrationToSendToTransmitter: Calibration?, firmware: String?, isFireFly: Bool) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "DEXCOM" + transmitterID[transmitterID.index(transmitterID.startIndex, offsetBy: 4)..<transmitterID.endIndex])
@ -145,6 +167,15 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
// initialize firmware
self.firmware = firmware
// assign calibrationToSendToTransmitter
self.calibrationToSendToTransmitter = calibrationToSendToTransmitter
// assign sensorStartDate
self.sensorStartDate = sensorStartDate
// assign isFireflyNoMatterTransmitterId
self.isFireflyNoMatterTransmitterId = isFireFly
// initialize - CBUUID_Receive_Authentication.rawValue and CBUUID_Write_Control.rawValue will probably not be used in the superclass
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: CBUUID_Advertisement_G5, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_G5)], CBUUID_ReceiveCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Receive_Authentication.rawValue, CBUUID_WriteCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Write_Control.rawValue, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate)
@ -170,12 +201,10 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
let testdata = GlucoseData(timeStamp: Date(), glucoseLevelRaw: testAmount)
debuglogging("timestamp testdata = " + testdata.timeStamp.description + ", with amount = " + testAmount.description)
var testdataasarray = [testdata]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorAge: nil)
testAmount = testAmount + 1
}
// MARK: public functions
/// scale the rawValue, dependent on transmitter version G5 , G6 --
/// for G6, there's two possible scaling factors, depending on the firmware version. For G5 there's only one, firmware version independent
/// - parameters:
@ -190,13 +219,11 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
/// verifies if it's a firefly based on transmitterId, if >= 8G then considered to be a Firefly
public func isFireFly() -> Bool {
return transmitterId.uppercased().compare("8G") == .orderedDescending
/// to ask transmitter reset
func reset(requested:Bool) {
G5ResetRequested = requested
}
// MARK: - deinit
deinit {
@ -279,7 +306,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
// is it a firefly ?
// if no treat it as a G5, with own calibration
if !isFireFly() {
if !useFireflyFlow() {
// send SensorTxMessage to transmitter
getSensorData()
@ -341,11 +368,15 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
//check type of message and process according to type
if let firstByte = value.first {
if let opCode = DexcomTransmitterOpCode(rawValue: firstByte) {
trace(" opcode = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, opCode.description)
switch opCode {
case .authChallengeRx:
if let authChallengeRxMessage = AuthChallengeRxMessage(data: value) {
// if not paired, then send message to delegate
@ -363,7 +394,9 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
// subscribe to writeControlCharacteristic
if let writeControlCharacteristic = writeControlCharacteristic {
setNotifyValue(true, for: writeControlCharacteristic)
} else {
trace(" writeControlCharacteristic is nil, can not set notifyValue", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
}
@ -386,6 +419,12 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
case .authRequestRx:
if letDexcomAppDoTheTransmitWork {
trace(" authRequestRx, letDexcomAppDoTheTransmitWork = true, no further processing", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return
}
if let authRequestRxMessage = AuthRequestRxMessage(data: value), let receiveAuthenticationCharacteristic = receiveAuthenticationCharacteristic {
guard let challengeHash = CGMG5Transmitter.computeHash(transmitterId, of: authRequestRxMessage.challenge) else {
@ -405,6 +444,11 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
case .sensorDataRx:
if letDexcomAppDoTheTransmitWork {
trace(" sensorDataRx, letDexcomAppDoTheTransmitWork = true, no further processing", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return
}
// if this is the first sensorDataRx after a successful pairing, then inform delegate that pairing is finished
if waitingPairingConfirmation {
waitingPairingConfirmation = false
@ -461,7 +505,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
var glucoseDataArray = [glucoseData]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorAge: nil)
}
@ -481,14 +525,14 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
processBatteryStatusRxMessage(value: value)
// if firefly continue with the firefly message flow
if isFireFly() { fireflyMessageFlow() }
if useFireflyFlow() { fireflyMessageFlow() }
case .transmitterVersionRx:
processTransmitterVersionRxMessage(value: value)
// if firefly continue with the firefly message flow
if isFireFly() { fireflyMessageFlow() }
if useFireflyFlow() { fireflyMessageFlow() }
case .keepAliveRx:
@ -497,6 +541,11 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
case .pairRequestRx:
if letDexcomAppDoTheTransmitWork {
trace(" pairRequestRx, letDexcomAppDoTheTransmitWork = true, no further processing", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return
}
// don't know if the user accepted the pairing request or not, we can only know by trying to subscribe to writeControlCharacteristic - if the device is paired, we'll receive a sensorDataRx message, if not paired, then a disconnect will happen
// set status to waitingForPairingConfirmation
@ -514,21 +563,29 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
processTransmitterTimeRxMessage(value: value)
// if firefly continue with the firefly message flow
if isFireFly() { fireflyMessageFlow() }
if useFireflyFlow() { fireflyMessageFlow() }
case .glucoseBackfillRx:
processGlucoseBackfillRxMessage(value: value)
// if firefly continue with the firefly message flow
if isFireFly() { fireflyMessageFlow() }
if useFireflyFlow() { fireflyMessageFlow() }
case .glucoseRx:
processGlucoseDataRxMessage(value: value)
// if firefly continue with the firefly message flow
if isFireFly() { fireflyMessageFlow() }
if useFireflyFlow() { fireflyMessageFlow() }
case .glucoseG6Rx:
// received when relying on official Dexcom app, ie in mode letDexcomAppDoTheTransmitWork = true
processGlucoseG6DataRxMessage(value: value)
// if firefly continue with the firefly message flow
if useFireflyFlow() { fireflyMessageFlow() }
case .calibrateGlucoseRx:
@ -537,6 +594,10 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
case .sessionStopRx:
processSessionStopRxMessage(value: value)
case .sessionStartRx:
processSessionStartRxMessage(value: value)
default:
trace(" unknown opcode received ", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
@ -558,6 +619,11 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
override func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if letDexcomAppDoTheTransmitWork {
trace("didDiscover, letDexcomAppDoTheTransmitWork = true, no further processing", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return
}
if Date() < Date(timeInterval: ConstantsDexcomG5.minimumTimeBetweenTwoReadings, since: timeStampOfLastG5Reading) {
// will probably never come here because reconnect doesn't happen with scanning, hence diddiscover will never be called except the very first time that an app tries to connect to a G5
trace("diddiscover peripheral, but last reading was less than %{public}@ minutes ago, will ignore", log: log, category: ConstantsLog.categoryCGMG5, type: .info, ConstantsDexcomG5.minimumTimeBetweenTwoReadings.minutes.description)
@ -576,7 +642,9 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
trace("connected to peripheral with name %{public}@, but last reading was less than %{public}@ minute ago", log: log, category: ConstantsLog.categoryCGMG5, type: .info, (deviceName != nil ? deviceName! : "unknown"), ConstantsDexcomG5.minimumTimeBetweenTwoReadings.minutes.description)
// don't disconnect here, keep the connection open, the transmitter will disconnect in a few seconds, assumption is that this will increase battery life
} else {
super.centralManager(central, didConnect: peripheral)
}
// to be sure waitingPairingConfirmation is reset to false
@ -631,46 +699,76 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
}
// MARK: CGMTransmitter protocol functions
/// overrides writeDataToPeripheral and checks if letDexcomAppDoTheTransmitWork is true and if so, data is not written to characteristic
override func writeDataToPeripheral(data: Data, characteristicToWriteTo: CBCharacteristic, type: CBCharacteristicWriteType) -> Bool {
if !letDexcomAppDoTheTransmitWork {
return super.writeDataToPeripheral(data: data, characteristicToWriteTo: characteristicToWriteTo, type: type)
} else {
trace("in writeDataToPeripheral, letDexcomAppDoTheTransmitWork - data not written to characteristic", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return true
}
}
/// to ask transmitter reset
func reset(requested:Bool) {
G5ResetRequested = requested
}
/// this transmitter does not support Libre non fixed slopes
func setNonFixedSlopeEnabled(enabled: Bool) {
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
// MARK: - CGMTransmitter protocol functions
func cgmTransmitterType() -> CGMTransmitterType {
return .dexcomG5
}
func isNonFixedSlopeEnabled() -> Bool {
return false
}
func isWebOOPEnabled() -> Bool {
return false
}
func requestNewReading() {
// not supported for Dexcom G5
}
func maxSensorAgeInDays() -> Int? {
func startSensor(dexcomCalibrationParameters: DexcomCalibrationParameters, startDate: Date) {
// no max sensor age for Dexcom
return nil
// assign sensorStartToSendToTransmitter to nil, because a new sensor start command must be sent
sensorStartToSendToTransmitter = nil
guard startDate != sensorStartDate else {
trace("in startSensor, but startDate is equal to already known sensorStartDate, so it looks as if a startSensor is done for a sensor that is already started. No further processing", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return
}
// assign sensorStartToSendToTransmitter
sensorStartToSendToTransmitter = (startDate, dexcomCalibrationParameters)
}
// MARK: helper functions
func calibrate(calibration: Calibration?) {
// - if calibration is valid, then it assigns calibrationToSendToTransmitter to calibration, will be picked up when transmitter connects , and if it's within an hour
// - the function does not immediately send the calibration to the transmitter, this will happen only if connected, after successful authentication
// set calibrationToSendToTransmitter to nil, because a new calibration needs to be sent
calibrationToSendToTransmitter = nil
guard let calibration = calibration else {
trace("in calibrate. assigned calibrationToSendToTransmitter to nil ", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return
}
if calibrationIsValid(calibration: calibration) {
calibrationToSendToTransmitter = calibration
trace("in calibrate. New calibration stored. value = %{public}@, timestamp = %{public}@ ", log: log, category: ConstantsLog.categoryCGMG5, type: .info, calibration.bg.description, calibration.timeStamp.toString(timeStyle: .long, dateStyle: .none))
}
}
// MARK: - private helper functions
/// sends SensorTxMessage to transmitter
private func getSensorData() {
@ -747,6 +845,44 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
/// sends calibrationTxMessage. Will use super.writeDataToPeripheral, meaning, even if letDexcomAppDoTheTransmitWork = true, then still it will send the data to the transmitter
private func sendCalibrationTxMessage(calibration: Calibration, transmitterStartDate: Date) {
let calibrationTxMessage = DexcomCalibrationTxMessage(glucose: Int(calibration.bg), timeStamp: calibration.timeStamp, transmitterStartDate: transmitterStartDate)
if let writeControlCharacteristic = writeControlCharacteristic {
trace("sending calibrationTxMessage with timestamp %{public}@, value %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, calibration.timeStamp.toString(timeStyle: .long, dateStyle: .long), calibration.bg.description)
_ = super.writeDataToPeripheral(data: calibrationTxMessage.data, characteristicToWriteTo: writeControlCharacteristic, type: .withResponse)
} else {
trace("in sendCalibrationTxMessage, writeControlCharacteristic is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
}
}
/// sends startDate and dexcomCalibrationParameters to transmitter. Will use super.writeDataToPeripheral, meaning, even if letDexcomAppDoTheTransmitWork = true, then still it will send the data to the transmitter
private func sendSessionStartTxMessage(sensorStartToSendToTransmitter: (startDate: Date, dexcomCalibrationParameters: DexcomCalibrationParameters), transmitterStartDate: Date) {
let sessionStartTxMessage = DexcomSessionStartTxMessage(startDate: sensorStartToSendToTransmitter.startDate, transmitterStartDate: transmitterStartDate, dexcomCalibrationParameters: sensorStartToSendToTransmitter.dexcomCalibrationParameters)
if let writeControlCharacteristic = writeControlCharacteristic {
trace("sending sessionStartTxMessage with startDate %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, sensorStartToSendToTransmitter.startDate.toString(timeStyle: .long, dateStyle: .long))
_ = super.writeDataToPeripheral(data: sessionStartTxMessage.data, characteristicToWriteTo: writeControlCharacteristic, type: .withResponse)
} else {
trace("in sendSessionStartTxMessage, writeControlCharacteristic is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
}
}
/// sends backfillTxMessage to transmitter
private func sendBackfillTxMessage(startTime: Date, endTime: Date, transmitterStartDate: Date) {
@ -829,7 +965,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
cGMG5TransmitterDelegate?.received(transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), cGMG5Transmitter: self)
// cgmTransmitterDelegate , because rootviewcontroller also shows battery info in home screen
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorAge: nil)
} else {
@ -859,7 +995,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
if let dexcomSessionStopRxMessage = DexcomSessionStopRxMessage(data: value) {
trace("in processSessionStopRxMessage, received dexcomSessionStopRxMessage, isOkay = %{public}@, received = %{public}@, sessionStartTime = %{public}@, sessionStopTime = %{public}@, status = %{public}@, transmitterTime = %{public}@,", log: log, category: ConstantsLog.categoryCGMG5, type: .info, dexcomSessionStopRxMessage.isOkay.description, dexcomSessionStopRxMessage.received.description, dexcomSessionStopRxMessage.sessionStartTime.description, dexcomSessionStopRxMessage.sessionStopTime.description, dexcomSessionStopRxMessage.status.description, dexcomSessionStopRxMessage.transmitterTime.description)
trace("in processSessionStopRxMessage, received dexcomSessionStopRxMessage, isOkay = %{public}@, received = %{public}@, sessionStartTime = %{public}@, sessionStopTime = %{public}@, status = %{public}@, transmitterTime = %{public}@,", log: log, category: ConstantsLog.categoryCGMG5, type: .info, dexcomSessionStopRxMessage.isOkay.description, dexcomSessionStopRxMessage.sessionStopResponse.description, dexcomSessionStopRxMessage.sessionStartTime.description, dexcomSessionStopRxMessage.sessionStopTime.description, dexcomSessionStopRxMessage.status.description, dexcomSessionStopRxMessage.transmitterTime.description)
} else {
trace("dexcomSessionStopRxMessage is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
@ -867,20 +1003,20 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
/// processSessionStartRxMessage
/// process sessionStartRxMessage
private func processSessionStartRxMessage(value: Data) {
if let dexcomSessionStartRxMessage = DexcomSessionStartRxMessage(data: value) {
trace("in processSessionStartRxMessage, received dexcomSessionStartRxMessage, info = %{public}@, requestedStartTime = %{public}@, sessionStartTime = %{public}@, status = %{public}@, transmitterTime = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info,
dexcomSessionStartRxMessage.info.description,
trace("in processSessionStartRxMessage, received dexcomSessionStartRxMessage, sessionStartResponse = %{public}@, requestedStartTime = %{public}@, sessionStartTime = %{public}@, status = %{public}@, transmitterTime = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info,
dexcomSessionStartRxMessage.sessionStartResponse.description,
dexcomSessionStartRxMessage.requestedStartTime.description,
dexcomSessionStartRxMessage.sessionStartTime.description,
dexcomSessionStartRxMessage.status.description,
dexcomSessionStartRxMessage.transmitterTime.description)
} else {
trace("dexcomSessionStartRxMessage is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
trace("in processSessionStartRxMessage, dexcomSessionStartRxMessage is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
}
}
@ -903,22 +1039,68 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
}
/// process glucoseRxMessage
private func processGlucoseDataRxMessage(value: Data) {
/// - used by processGlucoseDataRxMessage and processGlucoseG6DataRxMessage
/// - verifies the algorithmStatus and if ok, creates lastGlucoseInSensorDataRxReading
/// - parameters;
/// - calculatedValue : the value in the reading
/// - algorithmStatus : algorithm status
/// - timeStamp : timestamp in the reading
private func processGlucoseG6DataRxMessageOrGlucoseDataRxMessage(calculatedValue: Double, algorithmStatus: DexcomAlgorithmState, timeStamp: Date) {
if let glucoseDataRxMessage = GlucoseDataRxMessage(data: value) {
switch algorithmStatus {
trace("in processGlucoseDataRxMessage, received glucoseDataRxMessage, value = %{public}@, algorithm state = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, glucoseDataRxMessage.calculatedValue.description, glucoseDataRxMessage.status.description)
case .okay, .needsCalibration:
// create glucose data and assign to lastGlucoseInSensorDataRxReading
lastGlucoseInSensorDataRxReading = GlucoseData(timeStamp: Date(), glucoseLevelRaw: glucoseDataRxMessage.calculatedValue)
lastGlucoseInSensorDataRxReading = GlucoseData(timeStamp: timeStamp, glucoseLevelRaw: calculatedValue)
break
default:
trace(" algorithm state is %{public}@, not creating last glucoseData", log: log, category: ConstantsLog.categoryCGMG5, type: .info, algorithmStatus.description)
// for safety assign nil to lastGlucoseInSensorDataRxReading
lastGlucoseInSensorDataRxReading = nil
}
// don't send reading to delegate, will be done when transmitter disconnects, then we're sure we also received al necessary backfill data
// don't send reading to delegate, will be done when transmitter disconnects, then we're sure we also received al necessary backfill data
}
/// process glucoseRxMessage
private func processGlucoseDataRxMessage(value: Data) {
if let glucoseDataRxMessage = GlucoseDataRxMessage(data: value) {
trace("in processGlucoseDataRxMessage, received glucoseDataRxMessage, value = %{public}@, algorithm status = %{public}@, transmitter status = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, glucoseDataRxMessage.calculatedValue.description, glucoseDataRxMessage.algorithmStatus.description, glucoseDataRxMessage.transmitterStatus.description)
processGlucoseG6DataRxMessageOrGlucoseDataRxMessage(calculatedValue: glucoseDataRxMessage.calculatedValue, algorithmStatus: glucoseDataRxMessage.algorithmStatus, timeStamp: Date())
} else {
trace("glucoseDataRxMessage is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
trace("in processGlucoseDataRxMessage, glucoseDataRxMessage is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
}
}
/// process glucoseG6RxMessage
private func processGlucoseG6DataRxMessage(value: Data) {
if let transmitterStartDate = transmitterStartDate, let glucoseDataRxMessage = DexcomG6GlucoseDataRxMessage(data: value, transmitterStartDate: transmitterStartDate) {
trace("in processGlucoseG6DataRxMessage, received glucoseDataRxMessage, value = %{public}@, timeStamp = %{public}@, algorithmState = %{public}@, transmitterStatus = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, glucoseDataRxMessage.calculatedValue.description, glucoseDataRxMessage.timeStamp.toString(timeStyle: .long, dateStyle: .none), glucoseDataRxMessage.algorithmStatus.description,
glucoseDataRxMessage.transmitterStatus.description)
processGlucoseG6DataRxMessageOrGlucoseDataRxMessage(calculatedValue: glucoseDataRxMessage.calculatedValue, algorithmStatus: glucoseDataRxMessage.algorithmStatus, timeStamp: glucoseDataRxMessage.timeStamp)
} else {
trace("processGlucoseG6DataRxMessage is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .error)
}
@ -931,15 +1113,28 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
trace("in processTransmitterTimeRxMessage", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
if let sensorStartDate = transmitterTimeRxMessage.sensorStartDate {
if let receivedSensorStartDate = transmitterTimeRxMessage.sensorStartDate {
trace(" sensorStartDate = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, sensorStartDate.toString(timeStyle: .long, dateStyle: .long))
trace(" receivedSensorStartDate = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, receivedSensorStartDate.toString(timeStyle: .long, dateStyle: .long))
// send to delegate
cGMG5TransmitterDelegate?.received(sensorStartDate: sensorStartDate, cGMG5Transmitter: self)
cGMG5TransmitterDelegate?.received(sensorStartDate: receivedSensorStartDate, cGMG5Transmitter: self)
// set timeStampLastSensorStartTimeRead
timeStampLastSensorStartTimeRead = Date()
// if current sensorStartDate is < from receivedSensorStartDate then it seems a new sensor
if sensorStartDate == nil || (sensorStartDate! < receivedSensorStartDate) {
trace(" sensorStartDate == nil or sensorStartDate < receivedSensorStartDate, seems a new sensor is detected.", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: receivedSensorStartDate)
}
// assign sensorStartDate to receivedSensorStartDate
sensorStartDate = receivedSensorStartDate
} else {
trace(" sensorStartDate is nil", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
}
@ -1078,7 +1273,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
// if firefly, then subscribe to backfillCharacteristic
if isFireFly(), let backfillCharacteristic = backfillCharacteristic {
if useFireflyFlow(), let backfillCharacteristic = backfillCharacteristic {
trace(" calling setNotifyValue true for characteristic %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, CBUUID_Characteristic_UUID.CBUUID_Backfill.description)
@ -1097,9 +1292,9 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
private func fireflyMessageFlow() {
// first of all check that the transmitter is really a firefly, if not stop processing
if !isFireFly() { return }
if !useFireflyFlow() { return }
// treat it as a firefly, ie using the calibration on the sensor
// check if firmware is known, if not ask it
guard firmware != nil else {
sendTransmitterVersionTxMessage()
@ -1109,29 +1304,47 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
}
// check if battery status update needed, if not proceed with flow
// if yes, do no further processing (function batteryStatusRequested will also request battery Status if needed)
// if yes, request battery status (done in function batteryStatusRequested)
if !batteryStatusRequested() {
// check if transmitterStartDate is known, and if we've recently requested the sensorStartTime
// check if transmitterStartDate is known, and if we've recently requested the sensorStartTime, if not then request transmitterStartDate, response should contain both transmitterStartDate and sensorStartDate
if let transmitterStartDate = transmitterStartDate, Date() < Date(timeInterval: ConstantsDexcomG5.sensorStartTimeReadPeriod, since: timeStampLastSensorStartTimeRead) {
// if there's a sensor start command to send it
if let sensorStartToSendToTransmitter = sensorStartToSendToTransmitter {
sendSessionStartTxMessage(sensorStartToSendToTransmitter: (startDate: sensorStartToSendToTransmitter.startDate, dexcomCalibrationParameters: sensorStartToSendToTransmitter.dexcomCalibrationParameters), transmitterStartDate: transmitterStartDate)
} else
// if there's a valid calibrationToSendToTransmitter
if let calibrationToSendToTransmitter = calibrationToSendToTransmitter, calibrationIsValid(calibration: calibrationToSendToTransmitter) {
sendCalibrationTxMessage(calibration: calibrationToSendToTransmitter, transmitterStartDate: transmitterStartDate)
} else
// if glucoseTx was not yet sent and minimumTimeBetweenTwoReadings smaller than now - timeStampOfLastG5Reading (for safety)
// then send glucoseTx message
if !glucoseTxSent || Date() < Date(timeInterval: ConstantsDexcomG5.minimumTimeBetweenTwoReadings, since: timeStampOfLastG5Reading) {
// ask latest glucose value
sendGlucoseTxMessage()
trace(" did send glucoseTxMessage, setting glucoseTxSent to true", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
trace("in fireflyMessageFlow, did send glucoseTxMessage, setting glucoseTxSent to true", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
// next time don't ask again for glucoseTx, but ask for backfill if needed
glucoseTxSent = true
// check if backfill needed, and request backfill if needed
// if not needed continue with flow
} else if Date().timeIntervalSince(timeStampOfLastG5Reading) > ConstantsDexcomG5.minPeriodOfLatestReadingsToStartBackFill && !backfillTxSent {
} else
// check if backfill needed, and request backfill if needed
// if not needed continue with flow
if Date().timeIntervalSince(timeStampOfLastG5Reading) > ConstantsDexcomG5.minPeriodOfLatestReadingsToStartBackFill && !backfillTxSent {
// send backfillTxMessage
// start time = timeStampOfLastG5Reading, maximum maxBackfillPeriod
// start time = timeStampOfLastG5Reading - maximum maxBackfillPeriod
// end time = now
sendBackfillTxMessage(startTime: max(timeStampOfLastG5Reading, Date(timeIntervalSinceNow: -ConstantsDexcomG5.maxBackfillPeriod)), endTime: Date(), transmitterStartDate: transmitterStartDate)
@ -1166,7 +1379,7 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
/// - reset backFillStream, lastGlucoseInSensorDataRxReading, backfillTxSent, glucoseTxSent
private func sendGlucoseDataToDelegate() {
guard isFireFly() else {return}
guard useFireflyFlow() else {return}
// transmitterDate should be non nil
guard let transmitterStartDate = transmitterStartDate else {
@ -1174,53 +1387,53 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
return
}
// initialize glucoseDataArray, this array will contain the glucose values in the stream
trace("in sendGlucoseDataToDelegate", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
// initialize glucoseDataArray, in this array we will store the glucose values in the backfillStream and also othe lastGlucoseInSensorDataRxReading
var glucoseDataArray = [GlucoseData]()
trace("in sendGlucoseDataToDelegate", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
trace(" start processing backfillstream", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
// decode backfill stream
let backFills = backFillStream.decode()
// iterate through backfill's
for backFill in backFillStream.decode() {
let backfillDate = transmitterStartDate + Double(backFill.dexTime)
let diff = Date().timeIntervalSince1970 - backfillDate.timeIntervalSince1970
guard diff > 0, diff < TimeInterval.hours(6) else { continue }
glucoseDataArray.insert(GlucoseData(timeStamp: backfillDate, glucoseLevelRaw: Double(backFill.glucose)), at: 0)
// assign timeStampOfLastG5Reading to backfillDate if backfillDate is more recent
timeStampOfLastG5Reading = max(timeStampOfLastG5Reading, backfillDate)
trace(" new backfill, value = %{public}@, date = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, backFill.glucose.description, backfillDate.toString(timeStyle: .long, dateStyle: .none))
if backFills.count > 0 {
trace(" start processing backfillstream", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
// iterate through backfill's
for backFill in backFills {
let backfillDate = transmitterStartDate + Double(backFill.dexTime)
let diff = Date().timeIntervalSince1970 - backfillDate.timeIntervalSince1970
// readings older dan maxBackfillPeriod are ignored
guard diff > 0, diff < TimeInterval.hours(ConstantsDexcomG5.maxBackfillPeriod) else { continue }
glucoseDataArray.insert(GlucoseData(timeStamp: backfillDate, glucoseLevelRaw: Double(backFill.glucose)), at: 0)
trace(" new backfill, value = %{public}@, date = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, backFill.glucose.description, backfillDate.toString(timeStyle: .long, dateStyle: .none))
}
}
// - add lastGlucoseInSensorDataRxReading, which should already have been received
// - this reading is already sent to the delegate (while receiving glucoseRx message), but it's sent again. The delegate needs to reprocess the whole range and fill up gaps in the graph, if needed
// - add lastGlucoseInSensorDataRxReading, which should already have been received in GlucoseDataRxMessage or DexcomG6GlucoseDataRxMessage
if let lastGlucoseInSensorDataRxReading = lastGlucoseInSensorDataRxReading {
trace(" adding glucose data that was received in GlucoseDataRxMessage/DexcomG6GlucoseDataRxMessage, value = %{public}@, date = %{public}@", log: log, category: ConstantsLog.categoryCGMG5, type: .info, lastGlucoseInSensorDataRxReading.glucoseLevelRaw.description, lastGlucoseInSensorDataRxReading.timeStamp.toString(timeStyle: .long, dateStyle: .none))
glucoseDataArray.insert(lastGlucoseInSensorDataRxReading, at: 0)
// assign timeStampOfLastG5Reading to max of current value and timestamp of lastGlucoseInSensorDataRxReading
timeStampOfLastG5Reading = max(timeStampOfLastG5Reading, lastGlucoseInSensorDataRxReading.timeStamp)
}
// if glucoseDataArray contains data (which should be the case) then assign timeStampOfLastG5Reading to the most recent reading
if let lastGluoseReading = glucoseDataArray.first {
timeStampOfLastG5Reading = lastGluoseReading.timeStamp
}
if glucoseDataArray.count > 0 {
// assign timeStampOfLastG5Reading to the the timestamp of the most recent reading, which should be the first reading
timeStampOfLastG5Reading = glucoseDataArray.first!.timeStamp
trace(" calling cgmTransmitterInfoReceived with %{public}@ values", log: log, category: ConstantsLog.categoryCGMG5, type: .info, glucoseDataArray.count.description)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorAge: nil)
} else {
@ -1238,7 +1451,49 @@ class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
backfillTxSent = false
// reset glucoseTxSent to false
self.glucoseTxSent = false
glucoseTxSent = false
}
/// - check if stored calibration is valid or not, valid in the sense of "is it ok to send it to the transmitter"
///
/// - returns: false if calibration.sentToTransmitter is true, false if calibrationToSendToTransmitter has a timestamp in the future, false if calibrationToSendToTransmitter has an invalid value ( < 40 or > 400). true in all other cases
private func calibrationIsValid(calibration: Calibration) -> Bool {
trace("in calibrationIsValid", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
// if already sent to transmitter then invalid
if calibration.sentToTransmitter {
trace(" calibration.sentToTransmitter is true, means this calibration is already sent to the transmitter", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return false
}
// if calibrationToSendToTransmitter timestamp in future, then invalid
if Date().timeIntervalSince1970 - calibration.timeStamp.timeIntervalSince1970 < 0 {
trace(" calibration has timestamp in the future, not valid", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return false
}
// if calibrationToSendToTransmitter timestamp older than 1 hour then invalid
if Date().timeIntervalSince1970 - calibration.timeStamp.timeIntervalSince1970 > ConstantsDexcomG5.maxUnSentCalibrationAge {
trace(" calibration has timestamp older than %{public}@ hours, not valid", log: log, category: ConstantsLog.categoryCGMG5, type: .info, ConstantsDexcomG5.maxUnSentCalibrationAge.hours.description)
return false
}
// value out of range
if calibration.bg < 40 || calibration.bg > 400 {
trace(" calibration invalid value, lower than 40 or higher than 400", log: log, category: ConstantsLog.categoryCGMG5, type: .info)
return false
}
return true
}
/// verifies if it's a firefly based on transmitterId, if >= 8G then considered to be a Firefly
private func useFireflyFlow() -> Bool {
return transmitterId.uppercased().compare("8G") == .orderedDescending || isFireflyNoMatterTransmitterId
}

View File

@ -14,7 +14,7 @@ protocol CGMG5TransmitterDelegate: AnyObject {
/// transmitter reset result
func reset(for cGMG5Transmitter: CGMG5Transmitter, successful: Bool)
/// received sensor start time from transmitter
/// sensor start time as received from transmitter
func received(sensorStartDate: Date, cGMG5Transmitter: CGMG5Transmitter)
}

View File

@ -4,13 +4,19 @@ class CGMG6Transmitter: CGMG5Transmitter {
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID, 6 characters
/// - bluetoothTransmitterDelegate : a BluetoothTransmitterDelegate
/// - bluetoothTransmitterDelegate : a NluetoothTransmitterDelegate
/// - cGMTransmitterDelegate : a CGMTransmitterDelegate
init(address:String?, name: String?, transmitterID:String, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate, cGMG6TransmitterDelegate: CGMG6TransmitterDelegate, cGMTransmitterDelegate:CGMTransmitterDelegate, transmitterStartDate: Date?, firmware: String?) {
/// - cGMG5TransmitterDelegate : a CGMG5TransmitterDelegate
/// - transmitterStartDate : transmitter start date, optional - actual transmitterStartDate is received from transmitter itself, and stored in coredata. The stored value iss given here as parameter in the initializer. Means at app start up, it's read from core data and added here as parameter
/// - sensorStartDate : if the user starts the sensor via xDrip4iOS, then only after having receivec a confirmation from the transmitter, then sensorStartDate will be assigned to the actual sensor start date
/// - calibrationToSendToTransmitter : used to send calibration done by user via xDrip4iOS to Dexcom transmitter. For example, user may have give a calibration in the app, but it's not yet send to the transmitter. This needs to be verified in CGMG5Transmitter, which is why it's given here as parameter - when initializing, assign last known calibration for the active sensor, even if it's already sent.
/// - isFireFly : if true then the transmitter will be treated as a firefly, no matter the transmitter id, no matter if it's a G5, G6 or real Firefly
init(address:String?, name: String?, transmitterID:String, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate, cGMG6TransmitterDelegate: CGMG6TransmitterDelegate, cGMTransmitterDelegate:CGMTransmitterDelegate, transmitterStartDate: Date?, sensorStartDate: Date?, calibrationToSendToTransmitter: Calibration?, firmware: String?, isFireFly: Bool) {
// call super.init
super.init(address: address, name: name, transmitterID: transmitterID, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate, cGMG5TransmitterDelegate: cGMG6TransmitterDelegate, cGMTransmitterDelegate: cGMTransmitterDelegate, transmitterStartDate: transmitterStartDate, firmware: firmware)
super.init(address: address, name: name, transmitterID: transmitterID, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate, cGMG5TransmitterDelegate: cGMG6TransmitterDelegate, cGMTransmitterDelegate: cGMTransmitterDelegate, transmitterStartDate: transmitterStartDate, sensorStartDate: sensorStartDate, calibrationToSendToTransmitter: calibrationToSendToTransmitter, firmware: firmware, isFireFly: isFireFly)
}

View File

@ -0,0 +1,69 @@
//
// DexcomAlgorithmState.swift
// xdrip
//
// Created by Johan Degraeve on 12/11/2021.
// Copyright © 2021 Johan Degraeve. All rights reserved.
//
import Foundation
enum DexcomAlgorithmState: UInt8, CustomStringConvertible {
case None = 0x00
case SessionStopped = 0x01
case SensorWarmup = 0x02
case excessNoise = 0x03
case FirstofTwoBGsNeeded = 0x04
case SecondofTwoBGsNeeded = 0x05
case okay = 0x06
case needsCalibration = 0x07
case CalibrationError1 = 0x08
case CalibrationError2 = 0x09
case CalibrationLinearityFitFailure = 0x0A
case SensorFailedDuetoCountsAberration = 0x0B
case SensorFailedDuetoResidualAberration = 0x0C
case OutOfCalibrationDueToOutlier = 0x0D
case OutlierCalibrationRequest = 0x0E
case SessionExpired = 0x0F
case SessionFailedDueToUnrecoverableError = 0x10
case SessionFailedDueToTransmitterError = 0x11
case TemporarySensorIssue = 0x12
case SensorFailedDueToProgressiveSensorDecline = 0x13
case SensorFailedDueToHighCountsAberration = 0x14
case SensorFailedDueToLowCountsAberration = 0x15
case SensorFailedDueToRestart = 0x16
public var description: String {
switch self {
case .None: return "None"
case .SessionStopped: return "SessionStopped"
case .SensorWarmup: return "SensorWarmup"
case .excessNoise: return "excessNoise"
case .FirstofTwoBGsNeeded: return "FirstofTwoBGsNeeded"
case .SecondofTwoBGsNeeded: return "SecondofTwoBGsNeeded"
case .okay: return "InCalibration"
case .needsCalibration: return "needsCalibration"
case .CalibrationError1: return "CalibrationError1"
case .CalibrationError2: return "CalibrationError2"
case .CalibrationLinearityFitFailure: return "CalibrationLinearityFitFailure"
case .SensorFailedDuetoCountsAberration: return "SensorFailedDuetoCountsAberration"
case .SensorFailedDuetoResidualAberration: return "SensorFailedDuetoResidualAberration"
case .OutOfCalibrationDueToOutlier: return "OutOfCalibrationDueToOutlier"
case .OutlierCalibrationRequest: return "OutlierCalibrationRequest"
case .SessionExpired: return "SessionExpired"
case .SessionFailedDueToUnrecoverableError: return "SessionFailedDueToUnrecoverableError"
case .SessionFailedDueToTransmitterError: return "SessionFailedDueToTransmitterError"
case .TemporarySensorIssue: return "TemporarySensorIssue"
case .SensorFailedDueToProgressiveSensorDecline: return "SensorFailedDueToProgressiveSensorDecline"
case .SensorFailedDueToHighCountsAberration: return "SensorFailedDueToHighCountsAberration"
case .SensorFailedDueToLowCountsAberration: return "SensorFailedDueToLowCountsAberration"
case .SensorFailedDueToRestart: return "SensorFailedDueToRestart"
}
}
}

View File

@ -74,7 +74,7 @@ final class DexcomBackfillStream {
trend = data[idx]
idx += 1
if let state = DexcomCalibrationState(rawValue: type) {
if let state = DexcomAlgorithmState(rawValue: type) {
switch state {
case .okay, .needsCalibration:
if dexTime != 0 {

View File

@ -0,0 +1,114 @@
import Foundation
struct DexcomCalibrationParameters {
let parameter1: Int16
let parameter2: Int16
/// - sets parameter1 and parameter2 according to sensorCode
/// - if sensorCode == nil or "0000" then initialized with parameter1 and parameter2 equal to 0, meaning considered as a transmitter that doesn't need a sensorcode
init?(sensorCode: String?) {
guard let sensorCode = sensorCode else {
parameter1 = 0
parameter2 = 0
return
}
switch (sensorCode) {
// special null code
case "0000":
parameter1 = 0
parameter2 = 0
break
case "5915","9759":
parameter1 = 3100
parameter2 = 3600
break
case "5917","9357":
parameter1 = 3000
parameter2 = 3500
break
case "5931","9137":
parameter1 = 2900
parameter2 = 3400
break
case "5937","7197":
parameter1 = 2800
parameter2 = 3300
break
case "5951","9517":
parameter1 = 3100
parameter2 = 3500
break
case "5955","9179":
parameter1 = 3000
parameter2 = 3400
break
case "7171","7539":
parameter1 = 2700
parameter2 = 3300
break
case "9117","7135":
parameter1 = 2700
parameter2 = 3200
break
case "9159","5397":
parameter1 = 2600
parameter2 = 3200
break
case "9311","5391":
parameter1 = 2600
parameter2 = 3100
break
case "9371","5375":
parameter1 = 2500
parameter2 = 3100
break
case "9515","5795":
parameter1 = 2500
parameter2 = 3000
break
case "9551","5317":
parameter1 = 2400
parameter2 = 3000
break
case "9577","5177":
parameter1 = 2400
parameter2 = 2900
break
case "9713","5171":
parameter1 = 2300
parameter2 = 2900
break
default:
return nil
}
}
}

View File

@ -1,101 +0,0 @@
//
// DexcomG6CalibrationState.swift
// xDrip
//
// Created by Ivan Skoryk on 02.11.2020.
// Copyright © 2020 Faifly. All rights reserved.
//
// update for xDrip4iOS : renamed to DexcomCalibrationState
import Foundation
enum DexcomCalibrationState: UInt8 {
case unknown = 0x00
case stopped = 0x01
case warmingUp = 0x02
case excessNoise = 0x03
case needsFirstCalibration = 0x04
case needsSecondCalibration = 0x05
case okay = 0x06
case needsCalibration = 0x07
case calibrationConfused = 0x08
case calibrationConfused2 = 0x09
case needsDifferentCalibration = 0x0a
case sensorFailed = 0x0b
case sensorFailed2 = 0x0c
case unusualCalibration = 0x0d
case insufficientCalibration = 0x0e
case ended = 0x0f
case sensorFailed3 = 0x10
case transmitterProblem = 0x11
case errors = 0x12
case sensorFailed4 = 0x13
case sensorFailed5 = 0x14
case sensorFailed6 = 0x15
case sensorFailedStart = 0x16
}
extension DexcomCalibrationState {
static let stoppedCollection: [DexcomCalibrationState] = [
.stopped, .ended, .sensorFailed, .sensorFailed2,
.sensorFailed3, .sensorFailed4, .sensorFailed5,
.sensorFailed6, .sensorFailedStart
]
}
extension DexcomCalibrationState: CustomStringConvertible {
public var description: String {
switch self {
case .unknown:
return "unknown"
case .stopped:
return "stopped"
case .warmingUp:
return "warmingUp"
case .excessNoise:
return "excessNoise"
case .needsFirstCalibration:
return "needsFirstCalibration"
case .needsSecondCalibration:
return "needsSecondCalibration"
case .okay:
return "okay"
case .needsCalibration:
return "needsCalibration"
case .calibrationConfused:
return "calibrationConfused"
case .calibrationConfused2:
return "calibrationConfused2"
case .needsDifferentCalibration:
return "needsDifferentCalibration"
case .sensorFailed:
return "sensorFailed"
case .sensorFailed2:
return "sensorFailed2"
case .unusualCalibration:
return "unusualCalibration"
case .insufficientCalibration:
return "insufficientCalibration"
case .ended:
return "ended"
case .sensorFailed3:
return "sensorFailed3"
case .transmitterProblem:
return "transmitterProblem"
case .errors:
return "errors"
case .sensorFailed4:
return "sensorFailed4"
case .sensorFailed5:
return "sensorFailed5"
case .sensorFailed6:
return "sensorFailed6"
case .sensorFailedStart:
return "sensorFailedStart"
}
}
}

View File

@ -0,0 +1,36 @@
//
// DexcomG6CalibrationTxMessage.swift
// xDrip
//
// Created by Dmitry on 29.12.2020.
// Copyright © 2020 Faifly. All rights reserved.
//
import Foundation
struct DexcomCalibrationTxMessage: TransmitterTxMessage {
let data: Data
init(glucose: Int, timeStamp: Date, transmitterStartDate: Date) {
var array = [Int8]()
let time = Int(timeStamp.timeIntervalSince1970 - transmitterStartDate.timeIntervalSince1970)
array.append(Int8(DexcomTransmitterOpCode.calibrateGlucoseTx.rawValue))
withUnsafeBytes(of: glucose) {
array.append(contentsOf: Array($0.prefix(2 * MemoryLayout<Int8>.size)).map { Int8(bitPattern: $0) })
}
withUnsafeBytes(of: time) {
array.append(contentsOf: Array($0.prefix(4 * MemoryLayout<Int8>.size)).map { Int8(bitPattern: $0) })
}
let data = array.withUnsafeBufferPointer { Data(buffer: $0) }
self.data = data.appendingCRC()
}
}

View File

@ -0,0 +1,42 @@
import Foundation
struct DexcomG6GlucoseDataRxMessage {
let transmitterStatus: UInt8
let sequenceNumber: UInt32
let timeStamp: Date
let calculatedValue: Double
let algorithmStatus: DexcomAlgorithmState
init?(data: Data, transmitterStartDate: Date) {
guard data.count >= 16 else { return nil }
guard data.starts(with: .glucoseG6Rx) else { return nil }
transmitterStatus = data[1]
sequenceNumber = data[2..<6].toInt()
timeStamp = transmitterStartDate + TimeInterval(data.subdata(in: 6..<10).to(Int32.self))
calculatedValue = Double(data[10]) + Double(data[11]) * 256.0 + Double(data[13]) * 256.0 * 256.0
if let receivedState = DexcomAlgorithmState(rawValue: data[14]) {
algorithmStatus = receivedState
} else {
algorithmStatus = DexcomAlgorithmState.None
}
}
}

View File

@ -0,0 +1,39 @@
import Foundation
enum DexcomSessionStartResponse: UInt8 {
case manualCalibrationSessionStarted = 0x01
case staleStartComand = 0x02
case error = 0x03
case transmitterEndOfLife = 0x04
case autoCalibrationSessionInProgress = 0x05
public var description: String {
switch self {
case .manualCalibrationSessionStarted:
return "manualCalibrationSessionStarted"
case .staleStartComand:
return "staleStartComand"
case .error:
return "error"
case .transmitterEndOfLife:
return "transmitterEndOfLife"
case .autoCalibrationSessionInProgress:
return "autoCalibrationSessionInProgress"
}
}
}

View File

@ -12,21 +12,31 @@ import Foundation
struct DexcomSessionStartRxMessage {
let status: UInt8
let info: UInt8
let sessionStartResponse: DexcomSessionStartResponse
let requestedStartTime: Double
let sessionStartTime: Double
let transmitterTime: Double
init?(data: Data) {
guard data.count >= 15 else { return nil }
guard data.starts(with: .sessionStartTx) else {return nil}
guard data.starts(with: .sessionStartRx) else {return nil}
status = data[1]
info = data[2]
guard let sessionStartResponseReceived = DexcomSessionStartResponse(rawValue: data[2]) else {return nil}
sessionStartResponse = sessionStartResponseReceived
requestedStartTime = Double(Data(data[3..<7]).to(UInt32.self))
sessionStartTime = Double(Data(data[7..<11]).to(UInt32.self))
transmitterTime = Double(Data(data[11..<15]).to(UInt32.self))
}

View File

@ -0,0 +1,47 @@
//
// DexcomG6SessionStartTxMessage.swift
// xDrip
//
// Created by Dmitry on 08.01.2021.
// Copyright © 2021 Faifly. All rights reserved.
//
import Foundation
struct DexcomSessionStartTxMessage: TransmitterTxMessage {
let data: Data
init(startDate: Date, transmitterStartDate: Date, dexcomCalibrationParameters: DexcomCalibrationParameters) {
var array = [Int8]()
array.append(Int8(DexcomTransmitterOpCode.sessionStartTx.rawValue))
withUnsafeBytes(of: Int(startDate.timeIntervalSince1970 - transmitterStartDate.timeIntervalSince1970)) {
array.append(contentsOf: Array($0.prefix(4 * MemoryLayout<Int8>.size)).map { Int8(bitPattern: $0) })
}
withUnsafeBytes(of: Int(startDate.timeIntervalSince1970)) {
array.append(contentsOf: Array($0.prefix(4 * MemoryLayout<Int8>.size)).map { Int8(bitPattern: $0) })
}
if dexcomCalibrationParameters.parameter1 != 0 {
withUnsafeBytes(of: dexcomCalibrationParameters.parameter1) {
array.append(contentsOf: Array($0.prefix(2 * MemoryLayout<Int8>.size)).map { Int8(bitPattern: $0) })
}
withUnsafeBytes(of: dexcomCalibrationParameters.parameter2) {
array.append(contentsOf: Array($0.prefix(2 * MemoryLayout<Int8>.size)).map { Int8(bitPattern: $0) })
}
}
let data = array.withUnsafeBufferPointer { Data(buffer: $0) }
self.data = data.appendingCRC()
}
}

View File

@ -0,0 +1,30 @@
import Foundation
enum DexcomSessionStopResponse: UInt8 {
// 0x01 = Stop Session Successful | 0x02 = No Session In Progress | 0x03 Stale Stop Command
case stopSessionSuccessful = 0x01
case noSessionInProgress = 0x02
case staleStopCommand = 0x03
public var description: String {
switch self {
case .stopSessionSuccessful:
return "stopSessionSuccessful"
case .noSessionInProgress:
return "noSessionInProgress"
case .staleStopCommand:
return "staleStopCommand"
}
}
}

View File

@ -12,9 +12,13 @@ import Foundation
struct DexcomSessionStopRxMessage {
let status: UInt8
let received: UInt8
let sessionStopResponse: DexcomSessionStopResponse
let sessionStopTime: Double
let sessionStartTime: Double
let transmitterTime: Double
init?(data: Data) {
@ -24,9 +28,15 @@ struct DexcomSessionStopRxMessage {
guard data.starts(with: .sessionStopRx) else {return nil}
status = data[1]
received = data[2]
guard let sessionStoptResponseReceived = DexcomSessionStopResponse(rawValue: data[2]) else {return nil}
sessionStopResponse = sessionStoptResponseReceived
sessionStopTime = Double(Data(data[3..<7]).to(UInt32.self))
sessionStartTime = Double(Data(data[7..<11]).to(UInt32.self))
transmitterTime = Double(Data(data[11..<15]).to(UInt32.self))
}
@ -34,4 +44,5 @@ struct DexcomSessionStopRxMessage {
var isOkay: Bool {
return status == 0
}
}

View File

@ -2,11 +2,11 @@ import Foundation
struct GlucoseDataRxMessage {
let status: UInt8
let transmitterStatus: UInt8
let calculatedValue: Double
let state: DexcomCalibrationState
let algorithmStatus: DexcomAlgorithmState
init?(data: Data) {
@ -14,17 +14,17 @@ struct GlucoseDataRxMessage {
guard data.starts(with: .glucoseRx) else { return nil }
status = data[1]
transmitterStatus = data[1]
calculatedValue = Double(Data(data[10..<12]).to(UInt16.self) & 0xfff)
if let receivedState = DexcomCalibrationState(rawValue: data[12]) {
if let receivedState = DexcomAlgorithmState(rawValue: data[12]) {
state = receivedState
algorithmStatus = receivedState
} else {
state = DexcomCalibrationState.unknown
algorithmStatus = DexcomAlgorithmState.None
}

View File

@ -10,7 +10,8 @@ protocol CGMTransmitter:AnyObject {
/// --- for transmitters who support non fixed (all Libre transmitters) this should be implemented
func setNonFixedSlopeEnabled(enabled:Bool)
/// is the CGMTransmitter nonFixed enabled or not
/// - is the CGMTransmitter nonFixed enabled or not
/// - default implementation returns false
func isNonFixedSlopeEnabled() -> Bool
/// to set webOOPEnabled - called when user changes the setting
@ -19,7 +20,8 @@ protocol CGMTransmitter:AnyObject {
/// --- for transmitters who support webOOP (Bubble, MiaoMiao, ..) this should be implemented
func setWebOOPEnabled(enabled:Bool)
/// is the CGMTransmitter web oop enabled or not
/// - is the CGMTransmitter web oop enabled or not
/// - default implementation returns false
func isWebOOPEnabled() -> Bool
/// get cgmTransmitterType
@ -28,9 +30,25 @@ protocol CGMTransmitter:AnyObject {
/// only applicable for Libre transmitters. To request a new reading.
func requestNewReading()
/// maximum sensor age in minutes, nil if no maximum
/// - maximum sensor age in days, nil if no maximum
/// - default implementation returns nil
func maxSensorAgeInDays() -> Int?
/// - to send a start sensor command to the transmitter
/// - only useful for Dexcom - firefly type of transmitters, other transmitter types will have an empty implementation
/// - parameters:
/// - sensorCode : only to be filled in if code known, only applicable for Dexcom firefly
/// - startDate : sensor start timeStamp
func startSensor(sensorCode: String?, startDate: Date)
/// - to send a stop sensor command to the transmitter
/// - only useful for Dexcom type of transmitters, other transmitter types will have an empty implementation
func stopSensor(stopDate: Date)
/// - to send a calibration toe the transmitter
/// - only useful for Dexcom type of transmitters, other transmitter types will have an empty implementation
func calibrate(calibration: Calibration)
}
/// cgm transmitter types
@ -42,9 +60,13 @@ enum CGMTransmitterType:String, CaseIterable {
/// dexcom G5
case dexcomG5 = "Dexcom G5"
/// dexcom G6
/// - dexcom G6 - for non Firefly - although it can also be used for firefly
/// - only difference with firefly, is that sensorCode will not be asked (which is needed for firefly), and user will be asked to set a sensor start time (in case of firefly it's always the actual time that is used as start time)
case dexcomG6 = "Dexcom G6"
/// dexcom G6 firefly
case dexcomG6Firefly = "Dexcom G6 Firefly"
/// miaomiao
case miaomiao = "MiaoMiao"
@ -77,7 +99,7 @@ enum CGMTransmitterType:String, CaseIterable {
switch self {
case .dexcomG4, .dexcomG5, .dexcomG6 :
case .dexcomG4, .dexcomG5, .dexcomG6, .dexcomG6Firefly :
return .Dexcom
case .miaomiao, .Bubble, .GNSentry, .Droplet1, .blueReader, .watlaa, .Blucon, .Libre2, .Atom:
@ -91,7 +113,7 @@ enum CGMTransmitterType:String, CaseIterable {
///
/// 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
/// if true, then transmitterType must also be able to give the sensor age, ie sensorAge
func canDetectNewSensor() -> Bool {
switch self {
@ -102,6 +124,10 @@ enum CGMTransmitterType:String, CaseIterable {
case .dexcomG5, .dexcomG6:
return false
case .dexcomG6Firefly:
// for firefly, we receive the sensorStart time from the transmitter, this will be used to determine if a new sensor is received
return true
case .miaomiao, .Bubble:
return true
@ -139,6 +165,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .dexcomG4, .dexcomG5, .dexcomG6, .GNSentry, .Droplet1, .blueReader, .watlaa:
return true
case .dexcomG6Firefly:
return true
case .miaomiao, .Bubble, .Blucon, .Libre2, .Atom:
return true
@ -153,7 +182,7 @@ enum CGMTransmitterType:String, CaseIterable {
case .dexcomG4:
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelDexcomG4
case .dexcomG5, .dexcomG6:
case .dexcomG5, .dexcomG6, .dexcomG6Firefly:
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelDexcomG5
case .miaomiao:
@ -196,7 +225,7 @@ enum CGMTransmitterType:String, CaseIterable {
case .dexcomG4:
return ""
case .dexcomG5, .dexcomG6:
case .dexcomG5, .dexcomG6, .dexcomG6Firefly:
return "voltA"
case .miaomiao, .Bubble, .Droplet1:
@ -223,4 +252,66 @@ enum CGMTransmitterType:String, CaseIterable {
}
}
/// - if user starts, sensor, does it require a code?
/// - only true for Dexcom G6 firefly type of transmitters
func needsSensorStartCode() -> Bool {
switch self {
case .dexcomG6Firefly:
return true
default:
return false
}
}
/// - if user starts, sensor, does it require to give the start time?
/// - only false for Dexcom G6 type of transmitters - all other true
func needsSensorStartTime() -> Bool {
switch self {
case .dexcomG6Firefly:
return false
default:
return true
}
}
}
extension CGMTransmitter {
// empty implementation for transmitter types that don't need this
func setNonFixedSlopeEnabled(enabled: Bool) {}
// default implementation, false
func isNonFixedSlopeEnabled() -> Bool {return false}
// empty implementation for transmitter types that don't need this
func setWebOOPEnabled(enabled:Bool) {}
// default implementation, false
func isWebOOPEnabled() -> Bool {return false}
// empty implementation for transmitter types that don't need this
func requestNewReading() {}
// default implementation, nil
func maxSensorAgeInDays() -> Int? {return nil}
// default implementation, does nothing
func startSensor(sensorCode: String?, startDate: Date) {}
// default implementation, does nothing
func stopSensor(stopDate: Date) {}
// default implementation, does nothing
func calibrate(calibration: Calibration) {}
}

View File

@ -5,7 +5,9 @@ import CoreBluetooth
protocol CGMTransmitterDelegate:AnyObject {
/// only for transmitters that can detect new sensor
func newSensorDetected()
/// - parameters:
/// - detected sensor start time, optional, default nil
func newSensorDetected(sensorStartDate: Date?)
/// only for transmitters that can detect missing sensor
func sensorNotDetected()
@ -14,8 +16,8 @@ protocol CGMTransmitterDelegate:AnyObject {
/// - parameters:
/// - glucoseData : array of RawGlucoseData, can be empty array, first entry is the youngest
/// - transmitterBatteryInfo : needed for battery level alarm
/// - sensorTimeInMinutes : sensor age in minutes, only if transmitter can give that info, eg MiaoMiao, otherwise nil
func cgmTransmitterInfoReceived(glucoseData:inout [GlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorTimeInMinutes:Int?)
/// - sensorAge : only if transmitter can give that info, eg MiaoMiao, otherwise nil
func cgmTransmitterInfoReceived(glucoseData:inout [GlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorAge: TimeInterval?)
/// to pass some text error message, delegate can decide to show to user, log, ...
func errorOccurred(xDripError: XdripError)

View File

@ -262,7 +262,7 @@ class CGMAtomTransmitter:BluetoothTransmitter, CGMTransmitter {
let transmitterBatteryPercentage = Int(value[4])
// send transmitterBatteryInfo to cgmTransmitterDelegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: transmitterBatteryPercentage), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: transmitterBatteryPercentage), sensorAge: nil)
// send transmitter battery percentage to cGMAtomTransmitterDelegate
cGMAtomTransmitterDelegate?.received(batteryLevel: transmitterBatteryPercentage, from: self)
@ -354,8 +354,9 @@ class CGMAtomTransmitter:BluetoothTransmitter, CGMTransmitter {
self.sensorSerialNumber = libreSensorSerialNumber.serialNumber
// call delegate, to inform that a new sensor is detected
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
}
} else {

View File

@ -312,7 +312,7 @@ class CGMBluconTransmitter: BluetoothTransmitter {
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getPatchInfoRequest)
// by default set battery level to 100
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 100), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 100), sensorAge: nil)
cGMBluconTransmitterDelegate?.received(batteryLevel: 100, from: self)
@ -345,8 +345,9 @@ class CGMBluconTransmitter: BluetoothTransmitter {
sensorSerialNumber = newSerialNumber
// inform cGMTransmitterDelegate about new sensor detected
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
cGMBluconTransmitterDelegate?.received(serialNumber: sensorSerialNumber, from: self)
@ -372,7 +373,7 @@ class CGMBluconTransmitter: BluetoothTransmitter {
}
// inform cGMTransmitterDelegate about sensorSerialNumber and sensorState
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorAge: nil)
return
@ -407,7 +408,7 @@ class CGMBluconTransmitter: BluetoothTransmitter {
if valueAsString.startsWith(unknownCommand2BatteryLowIndicator) {
// this is considered as battery level 5%
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 5), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 5), sensorAge: nil)
cGMBluconTransmitterDelegate?.received(batteryLevel: 5, from: self)
@ -493,7 +494,7 @@ class CGMBluconTransmitter: BluetoothTransmitter {
let glucoseData = GlucoseData(timeStamp: Date(), glucoseLevelRaw: glucoseValue)
var glucoseDataArray = [glucoseData]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorAge: nil)
sendCommandToBlucon(opcode: .sleep)
@ -502,14 +503,14 @@ class CGMBluconTransmitter: BluetoothTransmitter {
case .bluconBatteryLowIndication1:
// this is considered as battery level 3%
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 3), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 3), sensorAge: nil)
cGMBluconTransmitterDelegate?.received(batteryLevel: 3, from: self)
case .bluconBatteryLowIndication2:
// this is considered as battery level 2%
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 2), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 2), sensorAge: nil)
cGMBluconTransmitterDelegate?.received(batteryLevel: 2, from: self)
@ -535,14 +536,6 @@ extension CGMBluconTransmitter: CGMTransmitter {
nonFixedSlopeEnabled = enabled
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
func setWebOOPSite(oopWebSite: String) {}
func setWebOOPToken(oopWebToken: String) {}
func cgmTransmitterType() -> CGMTransmitterType {
return .Blucon
}
@ -551,16 +544,5 @@ extension CGMBluconTransmitter: CGMTransmitter {
return nonFixedSlopeEnabled
}
func isWebOOPEnabled() -> Bool {
return false
}
func maxSensorAgeInDays() -> Int? {
// not supported for blucon
return nil
}
}

View File

@ -114,7 +114,7 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, CGMTransmitter {
// send to delegate
var glucoseDataArray = [GlucoseData(timeStamp: Date(), glucoseLevelRaw: rawDataAsDouble)]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transMitterBatteryInfo, sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transMitterBatteryInfo, sensorAge: nil)
} else {
trace(" value is nil, no further processing", log: log, category: ConstantsLog.categoryCGMBlueReader, type: .error)
@ -128,9 +128,6 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, CGMTransmitter {
nonFixedSlopeEnabled = enabled
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {}
func cgmTransmitterType() -> CGMTransmitterType {
return .blueReader
}
@ -139,19 +136,4 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, CGMTransmitter {
return nonFixedSlopeEnabled
}
func isWebOOPEnabled() -> Bool {
return false
}
func requestNewReading() {
// not supported for blucon
}
func maxSensorAgeInDays() -> Int? {
// not supported for bluereader
return nil
}
}

View File

@ -158,7 +158,7 @@ class CGMBubbleTransmitter:BluetoothTransmitter, CGMTransmitter {
cGMBubbleTransmitterDelegate?.received(batteryLevel: batteryPercentage, from: self)
// send batteryPercentage to delegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorAge: nil)
// store received firmware local
self.firmware = firmware
@ -248,8 +248,9 @@ class CGMBubbleTransmitter:BluetoothTransmitter, CGMTransmitter {
trace(" new sensor detected : %{public}@", log: log, category: ConstantsLog.categoryCGMBubble, type: .info, libreSensorSerialNumber.serialNumber)
// inform cgmTransmitterDelegate about new sensor detected
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
// inform cGMBubbleTransmitterDelegate about new sensor detected
cGMBubbleTransmitterDelegate?.received(serialNumber: libreSensorSerialNumber.serialNumber, from: self)

View File

@ -108,14 +108,14 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, CGMTransmitter {
}
// fourth field is sensor time in minutes, stop if convert to Int fails
guard let sensorTimeInMinutes = Int(String(valueAsString[valueAsString.index(after: indexesOfSplitter[2])..<valueAsString.endIndex])) else {
trace(" failed to convert sensorTimeInMinutes field to Int", log: log, category: ConstantsLog.categoryCGMDroplet1, type: .error)
guard let sensorAgeInMinutes = Int(String(valueAsString[valueAsString.index(after: indexesOfSplitter[2])..<valueAsString.endIndex])) else {
trace(" failed to convert sensorAge field to Int", log: log, category: ConstantsLog.categoryCGMDroplet1, type: .error)
return
}
// send glucoseDataArray, transmitterBatteryInfo and sensorTimeInMinutes to cgmTransmitterDelegate
// send glucoseDataArray, transmitterBatteryInfo and sensorAge to cgmTransmitterDelegate
var glucoseDataArray = [GlucoseData(timeStamp: Date(), glucoseLevelRaw: rawValueAsDouble)]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorTimeInMinutes: sensorTimeInMinutes * 10)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorAge: TimeInterval(minutes: Double(sensorAgeInMinutes * 10) ))
// send transmitterBatteryInfo to delegate
cGMDropletTransmitterDelegate?.received(batteryLevel: batteryPercentage, from: self)
@ -132,31 +132,12 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, CGMTransmitter {
nonFixedSlopeEnabled = enabled
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
func cgmTransmitterType() -> CGMTransmitterType {
return .Droplet1
}
func isWebOOPEnabled() -> Bool {
return false
}
func isNonFixedSlopeEnabled() -> Bool {
return nonFixedSlopeEnabled
}
func requestNewReading() {
// not supported for droplet
}
func maxSensorAgeInDays() -> Int? {
// no supported for droplet
return nil
}
}

View File

@ -229,7 +229,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, CGMTransmitter {
i = i + 1
}
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes))
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorAge: TimeInterval(minutes: Double(sensorElapsedTimeInMinutes)))
}
}
@ -239,9 +239,6 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: CGMTransmitter protocol functions
func setWebOOPEnabled(enabled: Bool) {
}
func setNonFixedSlopeEnabled(enabled: Bool) {
nonFixedSlopeEnabled = enabled
}
@ -250,25 +247,10 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, CGMTransmitter {
return .GNSentry
}
func isWebOOPEnabled() -> Bool {
return false
}
func isNonFixedSlopeEnabled() -> Bool {
return nonFixedSlopeEnabled
}
func requestNewReading() {
// not supported for GNSEntry
}
func maxSensorAgeInDays() -> Int? {
// not supported for gnsentry
return nil
}
// MARK: CBCentralManager overriden functions
override func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

View File

@ -293,10 +293,10 @@ class CGMLibre2Transmitter:BluetoothTransmitter, CGMTransmitter {
// if oop web not enabled, then don't pass libre1DerivedAlgorithmParameters
var parsedBLEData = Libre2BLEUtilities.parseBLEData(Data(try Libre2BLEUtilities.decryptBLE(sensorUID: sensorUID, data: rxBuffer)), libre1DerivedAlgorithmParameters: isWebOOPEnabled() ? UserDefaults.standard.libre1DerivedAlgorithmParameters : nil)
// send glucoseData and sensorTimeInMinutes to cgmTransmitterDelegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &parsedBLEData.bleGlucose, transmitterBatteryInfo: nil, sensorTimeInMinutes: Int(parsedBLEData.sensorTimeInMinutes))
// send glucoseData and sensorAge to cgmTransmitterDelegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &parsedBLEData.bleGlucose, transmitterBatteryInfo: nil, sensorAge: TimeInterval(minutes: Double(parsedBLEData.sensorTimeInMinutes)))
// send sensorTimeInMinutes also to cGMLibre2TransmitterDelegate
// send sensorAge also to cGMLibre2TransmitterDelegate
cGMLibre2TransmitterDelegate?.received(sensorTimeInMinutes: Int(parsedBLEData.sensorTimeInMinutes), from: self)
} catch {
@ -345,10 +345,6 @@ class CGMLibre2Transmitter:BluetoothTransmitter, CGMTransmitter {
return nonFixedSlopeEnabled
}
func requestNewReading() {
// not supported for Libre 2
}
func maxSensorAgeInDays() -> Int? {
return libreSensorType?.maxSensorAgeInDays()
@ -416,8 +412,9 @@ extension CGMLibre2Transmitter: LibreNFCDelegate {
self.sensorSerialNumber = receivedSensorSerialNumberAsString
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
cGMLibre2TransmitterDelegate?.received(serialNumber: receivedSensorSerialNumberAsString, from: self)
}

View File

@ -212,7 +212,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, CGMTransmitter {
cGMMiaoMiaoTransmitterDelegate?.received(batteryLevel: batteryPercentage, from: self)
// send batteryPercentage to delegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorAge: nil)
// get sensor serialNumber and if changed inform delegate
if let libreSensorSerialNumber = LibreSensorSerialNumber(withUID: Data(rxBuffer.subdata(in: 5..<13)), with: LibreSensorType.type(patchInfo: patchInfo)) {
@ -225,8 +225,9 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, CGMTransmitter {
trace(" new sensor detected : %{public}@", log: log, category: ConstantsLog.categoryCGMMiaoMiao, type: .info, libreSensorSerialNumber.serialNumber)
// inform delegate about new sensor detected
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
cGMMiaoMiaoTransmitterDelegate?.received(serialNumber: libreSensorSerialNumber.serialNumber, from: self)
}
@ -251,8 +252,9 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, CGMTransmitter {
case .newSensor:
trace("in peripheral didUpdateValueFor, new sensor detected", log: log, category: ConstantsLog.categoryCGMMiaoMiao, type: .info)
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
// send 0xD3 and 0x01 to confirm sensor change as defined in MiaoMiao protocol documentation
// after that send start reading command, each with delay of 500 milliseconds
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(500)) {

View File

@ -344,18 +344,24 @@ class LibreDataParser {
cgmTransmitterDelegate?.errorOccurred(xDripError: xDripError)
}
// variable to be used in last call to cgmTransmitterDelegate
var sensorTimeInResult: TimeInterval?
// if sensor time < 60, return an empty glucose data array
// should probably not happen because we only get here if status = .ready or .expired ?
if let sensorTimeInMinutes = result.sensorTimeInMinutes {
// assign sensorTimeInResult, will be used later
sensorTimeInResult = TimeInterval(minutes: Double(sensorTimeInMinutes))
guard sensorTimeInMinutes >= 60 else {
trace("in handleGlucoseData, sensorTimeInMinutes < 60 minutes, no further processing", log: log, category: ConstantsLog.categoryLibreDataParser, type: .info)
var emptyArray = [GlucoseData]()
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorTimeInMinutes: result.sensorTimeInMinutes)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorAge: TimeInterval(minutes: Double(sensorTimeInMinutes)))
// call completion handler to make sure the sensor state is handled, set state to .starting, because result.sensorState has value .ready here which is not correct
completionHandler(.starting, result.xDripError)
@ -367,8 +373,9 @@ class LibreDataParser {
}
var result = result
// call delegate with result
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: nil, sensorTimeInMinutes: result.sensorTimeInMinutes)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: nil, sensorAge: sensorTimeInResult)
completionHandler(result.sensorState, result.xDripError)

View File

@ -303,7 +303,8 @@ final class WatlaaBluetoothTransmitter: BluetoothTransmitter {
trace(" new sensor detected : %{public}@", log: log, category: ConstantsLog.categoryWatlaa, type: .info, libreSensorSerialNumber.serialNumber)
// inform delegate about new sensor detected
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
watlaaBluetoothTransmitterDelegate?.received(serialNumber: libreSensorSerialNumber.serialNumber, from: self)
@ -315,7 +316,7 @@ final class WatlaaBluetoothTransmitter: BluetoothTransmitter {
watlaaBluetoothTransmitterDelegate?.received(transmitterBatteryLevel: batteryPercentage, watlaaBluetoothTransmitter: self)
// send batteryPercentage to delegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorTimeInMinutes: nil)
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorAge: nil)
libreDataParser.libreDataProcessor(libreSensorSerialNumber: LibreSensorSerialNumber(withUID: Data(rxBuffer.subdata(in: 5..<13)), with: nil)?.serialNumber, patchInfo: nil, webOOPEnabled: webOOPEnabled, libreData: (rxBuffer.subdata(in: miaoMiaoHeaderLength..<(344 + miaoMiaoHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, dataIsDecryptedToLibre1Format: false, testTimeStamp: nil, completionHandler: { (sensorState: LibreSensorState?, xDripError: XdripError?) in
@ -350,8 +351,10 @@ final class WatlaaBluetoothTransmitter: BluetoothTransmitter {
// not sure if watlaa will ever send this, and if so if it will handle the response correctly
// this is copied from MiaoMiao
trace("in peripheral didUpdateValueFor, new sensor detected", log: log, category: ConstantsLog.categoryWatlaa, type: .info)
cgmTransmitterDelegate?.newSensorDetected()
// assign sensorStartDate, for this type of transmitter the sensorAge is passed in another call to cgmTransmitterDelegate
cgmTransmitterDelegate?.newSensorDetected(sensorStartDate: nil)
// send 0xD3 and 0x01 to confirm sensor change as defined in MiaoMiao protocol documentation
// after that send start reading command, each with delay of 500 milliseconds
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(500)) {

View File

@ -36,11 +36,4 @@ extension WatlaaBluetoothTransmitter: CGMTransmitter {
_ = sendStartReadingCommand()
}
func maxSensorAgeInDays() -> Int? {
// no max sensor age for Watlaa
return nil
}
}

View File

@ -20,4 +20,7 @@ enum ConstantsDexcomG5 {
/// if there's a new connect within this period, but latest reading was less than this interval ago, then no need to request new reading
static let minimumTimeBetweenTwoReadings = TimeInterval(minutes: 2.0)
/// specifically for firefly. If calibration was created more than this period ago, but not yet sent to the transmitter, then it will not be sent anymore
static let maxUnSentCalibrationAge = TimeInterval(hours: 1)
}

View File

@ -24,6 +24,9 @@ extension Calibration {
@NSManaged public var deviceName: String?
@NSManaged public var bgreadings: NSSet
@NSManaged public var sensor: Sensor
// only used for firefly transmitters, for now
@NSManaged public var sentToTransmitter: Bool
}

View File

@ -33,11 +33,15 @@ class CalibrationsAccessor {
/// get last calibration (ie youngest) for currently active sensor and with sensorconfidence and slopeconfidence != 0
/// - parameters:
/// - withActivesensor : should be currently active sensor
/// - withActivesensor : should be currently active sensor - if nil then returnvalue is also nil
/// - returns:
/// - the last calibration, can be nil
func lastCalibrationForActiveSensor(withActivesensor sensor:Sensor) -> Calibration? {
func lastCalibrationForActiveSensor(withActivesensor sensor:Sensor?) -> Calibration? {
guard let sensor = sensor else {return nil}
return getFirstOrLastCalibration(withActivesensor: sensor, first: false)
}
/// Returns last calibrations, possibly zero

View File

@ -43,6 +43,8 @@ public class Calibration: NSManagedObject {
possibleBad = false
id = UniqueId.createEventId()
sentToTransmitter = false
}
var sensorAgeAtTimeOfEstimation:Double {
@ -75,6 +77,7 @@ public class Calibration: NSManagedObject {
r += "\n" + indentation + "slope = " + slope.description
r += "\n" + indentation + "slopeConfidence = " + slopeConfidence.description
r += "\n" + indentation + "timestamp = " + timeStamp.description + "\n"
r += "\n" + indentation + "sentToTransmitter = " + sentToTransmitter.description + "\n"
return r
}
}

View File

@ -28,8 +28,12 @@ extension DexcomG5 {
@NSManaged public var isDexcomG6: Bool
@NSManaged public var isFirefly: Bool
@NSManaged public var transmitterStartDate: Date?
/// - contains sensor start date, received from transmitter
/// - if the user starts the sensor via xDrip4iOS, then only after having receivec a confirmation from the transmitter, then sensorStartDate will be assigned to the actual sensor start date
@NSManaged public var sensorStartDate: Date?
}

View File

@ -87,6 +87,7 @@
<attribute name="rawTimeStamp" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="rawValue" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="sensorConfidence" attributeType="Double" usesScalarValueType="YES"/>
<attribute name="sentToTransmitter" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="slope" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="slopeConfidence" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="timeStamp" attributeType="Date" usesScalarValueType="NO"/>
@ -103,6 +104,7 @@
<attribute name="batteryTemperature" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="firmwareVersion" optional="YES" attributeType="String"/>
<attribute name="isDexcomG6" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="isFirefly" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="lastResetTimeStamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="sensorStartDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="transmitterStartDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
@ -164,9 +166,9 @@
<element name="Blucon" positionX="-657" positionY="189" width="128" height="58"/>
<element name="BlueReader" positionX="-639" positionY="207" width="128" height="58"/>
<element name="Bubble" positionX="-657" positionY="189" width="128" height="88"/>
<element name="Calibration" positionX="-859.21484375" positionY="46.21484375" width="128" height="285"/>
<element name="Calibration" positionX="-859.21484375" positionY="46.21484375" width="128" height="284"/>
<element name="DexcomG4" positionX="-621" positionY="225" width="128" height="58"/>
<element name="DexcomG5" positionX="-648" positionY="198" width="128" height="209"/>
<element name="DexcomG5" positionX="-648" positionY="198" width="128" height="224"/>
<element name="Droplet" positionX="-630" positionY="216" width="128" height="58"/>
<element name="GNSEntry" positionX="-648" positionY="198" width="128" height="103"/>
<element name="Libre2" positionX="-648" positionY="198" width="128" height="58"/>

View File

@ -37,4 +37,9 @@ extension TimeInterval {
var hours: Double {
return minutes / 60.0
}
var days: Double {
return hours / 24.0
}
}

View File

@ -61,6 +61,12 @@ class BluetoothPeripheralManager: NSObject {
/// reference to BLEPeripheralAccessor
private var bLEPeripheralAccessor: BLEPeripheralAccessor
/// reference to SensorsAccessor
private var sensorsAccessor: SensorsAccessor
/// reference to CalibrationsAccessor
private var calibrationsAccessor: CalibrationsAccessor
/// to solve problem that sometemes UserDefaults key value changes is triggered twice for just one change
private let keyValueObserverTimeKeeper:KeyValueObserverTimeKeeper = KeyValueObserverTimeKeeper()
@ -77,6 +83,8 @@ class BluetoothPeripheralManager: NSObject {
// initialize properties
self.coreDataManager = coreDataManager
self.bgReadingsAccessor = BgReadingsAccessor(coreDataManager: coreDataManager)
self.sensorsAccessor = SensorsAccessor(coreDataManager: coreDataManager)
self.calibrationsAccessor = CalibrationsAccessor(coreDataManager: coreDataManager)
self.cgmTransmitterDelegate = cgmTransmitterDelegate
self.cgmTransmitterInfoChanged = cgmTransmitterInfoChanged
self.bLEPeripheralAccessor = BLEPeripheralAccessor(coreDataManager: coreDataManager)
@ -149,7 +157,7 @@ class BluetoothPeripheralManager: NSObject {
}
case .DexcomG5Type, .DexcomG6Type:
case .DexcomG5Type, .DexcomG6Type, .DexcomG6FireflyType:
// both DexcomG5Type and DexcomG6Type are stored in blePeripheral as dexcomG5
if let dexcomG5orG6 = blePeripheral.dexcomG5 {
@ -163,13 +171,16 @@ class BluetoothPeripheralManager: NSObject {
// create an instance of CGMG5Transmitter (or CGMG6Transmitter), CGMG5Transmitter (or CGMG6Transmitter) will automatically try to connect to the dexcom with the address that is stored in dexcom
// add it to the array of bluetoothTransmitters
if !dexcomG5orG6.isDexcomG6 {
if !dexcomG5orG6.isDexcomG6 && !dexcomG5orG6.isFirefly {
bluetoothTransmitters.insert(CGMG5Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG5TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, firmware: dexcomG5orG6.firmwareVersion), at: index)
bluetoothTransmitters.insert(CGMG5Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG5TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, sensorStartDate: dexcomG5orG6.sensorStartDate, calibrationToSendToTransmitter: calibrationsAccessor.lastCalibrationForActiveSensor(withActivesensor: sensorsAccessor.fetchActiveSensor()), firmware: dexcomG5orG6.firmwareVersion, isFireFly: dexcomG5orG6.isFirefly), at: index)
} else {
bluetoothTransmitters.insert(CGMG6Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, firmware: dexcomG5orG6.firmwareVersion), at: index)
// CGMG6 transmitter is created for G6 or Firefly
// in the end for firefly we could as wel create it as a G5
bluetoothTransmitters.insert(CGMG6Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, sensorStartDate: dexcomG5orG6.sensorStartDate, calibrationToSendToTransmitter: calibrationsAccessor.lastCalibrationForActiveSensor(withActivesensor: sensorsAccessor.fetchActiveSensor()), firmware: dexcomG5orG6.firmwareVersion, isFireFly: dexcomG5orG6.isFirefly), at: index)
}
@ -182,7 +193,7 @@ class BluetoothPeripheralManager: NSObject {
} else {
// bluetoothTransmitters array (which shoul dhave the same number of elements as bluetoothPeripherals) needs to have an empty row for the transmitter
// bluetoothTransmitters array (which should have the same number of elements as bluetoothPeripherals) needs to have an empty row for the transmitter
bluetoothTransmitters.insert(nil, at: index)
}
@ -513,7 +524,7 @@ class BluetoothPeripheralManager: NSObject {
// no need to send reading to watlaa in master mode
break
case .DexcomG5Type, .BubbleType, .MiaoMiaoType, .BluconType, .GNSentryType, .BlueReaderType, .DropletType, .DexcomG4Type, .DexcomG6Type, .Libre2Type, .AtomType:
case .DexcomG5Type, .BubbleType, .MiaoMiaoType, .BluconType, .GNSentryType, .BlueReaderType, .DropletType, .DexcomG4Type, .DexcomG6Type, .Libre2Type, .AtomType, .DexcomG6FireflyType:
// cgm's don't receive reading, they send it
break
@ -580,27 +591,29 @@ class BluetoothPeripheralManager: NSObject {
}
case .DexcomG5Type, .DexcomG6Type:
case .DexcomG5Type, .DexcomG6Type, .DexcomG6FireflyType:
if let dexcomG5orG6 = bluetoothPeripheral as? DexcomG5 {
if let transmitterId = dexcomG5orG6.blePeripheral.transmitterId, let cgmTransmitterDelegate = cgmTransmitterDelegate {
if !dexcomG5orG6.isDexcomG6 {
if !dexcomG5orG6.isDexcomG6 && !dexcomG5orG6.isFirefly {
newTransmitter = CGMG5Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG5TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, firmware: dexcomG5orG6.firmwareVersion)
newTransmitter = CGMG5Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG5TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, sensorStartDate: dexcomG5orG6.sensorStartDate, calibrationToSendToTransmitter: calibrationsAccessor.lastCalibrationForActiveSensor(withActivesensor: sensorsAccessor.fetchActiveSensor()), firmware: dexcomG5orG6.firmwareVersion, isFireFly: dexcomG5orG6.isFirefly)
} else {
newTransmitter = CGMG6Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, firmware: dexcomG5orG6.firmwareVersion)
// CGMG6 transmitter is created for G6 or Firefly
// in the end for firefly we could as wel create it as a G5
newTransmitter = CGMG6Transmitter(address: dexcomG5orG6.blePeripheral.address, name: dexcomG5orG6.blePeripheral.name, transmitterID: transmitterId, bluetoothTransmitterDelegate: self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: dexcomG5orG6.transmitterStartDate, sensorStartDate: dexcomG5orG6.sensorStartDate, calibrationToSendToTransmitter: calibrationsAccessor.lastCalibrationForActiveSensor(withActivesensor: sensorsAccessor.fetchActiveSensor()), firmware: dexcomG5orG6.firmwareVersion, isFireFly: dexcomG5orG6.isFirefly)
}
} else {
trace("in getBluetoothTransmitter, case DexcomG5Type or DexcomG6Type but transmitterId is nil or cgmTransmitterDelegate is nil, looks like a coding error ", log: log, category: ConstantsLog.categoryBluetoothPeripheralManager, type: .error)
trace("in getBluetoothTransmitter, case DexcomG5Type or DexcomG6Type or DexcomG6FireflyType but transmitterId is nil or cgmTransmitterDelegate is nil, looks like a coding error", log: log, category: ConstantsLog.categoryBluetoothPeripheralManager, type: .error)
}
}
@ -780,7 +793,7 @@ class BluetoothPeripheralManager: NSObject {
return .DexcomG5Type
}
case .DexcomG6Type:
case .DexcomG6Type, .DexcomG6FireflyType:
if bluetoothTransmitter is CGMG6Transmitter {
return .DexcomG6Type
}
@ -860,7 +873,7 @@ class BluetoothPeripheralManager: NSObject {
fatalError("in createNewTransmitter, type DexcomG5Type, transmitterId is nil or cgmTransmitterDelegate is nil")
}
return CGMG5Transmitter(address: nil, name: nil, transmitterID: transmitterId, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate ?? self, cGMG5TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: nil, firmware: nil)
return CGMG5Transmitter(address: nil, name: nil, transmitterID: transmitterId, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate ?? self, cGMG5TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: nil, sensorStartDate: nil, calibrationToSendToTransmitter: nil, firmware: nil, isFireFly: false)
case .DexcomG6Type:
@ -868,7 +881,15 @@ class BluetoothPeripheralManager: NSObject {
fatalError("in createNewTransmitter, type DexcomG6Type, transmitterId is nil or cgmTransmitterDelegate is nil")
}
return CGMG6Transmitter(address: nil, name: nil, transmitterID: transmitterId, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate ?? self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: nil, firmware: nil)
return CGMG6Transmitter(address: nil, name: nil, transmitterID: transmitterId, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate ?? self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: nil, sensorStartDate: nil, calibrationToSendToTransmitter: nil, firmware: nil, isFireFly: false)
case .DexcomG6FireflyType:
guard let transmitterId = transmitterId, let cgmTransmitterDelegate = cgmTransmitterDelegate else {
fatalError("in createNewTransmitter, type DexcomG6Type, transmitterId is nil or cgmTransmitterDelegate is nil")
}
return CGMG6Transmitter(address: nil, name: nil, transmitterID: transmitterId, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate ?? self, cGMG6TransmitterDelegate: self, cGMTransmitterDelegate: cgmTransmitterDelegate, transmitterStartDate: nil, sensorStartDate: nil, calibrationToSendToTransmitter: nil, firmware: nil, isFireFly: true)
case .BubbleType:
@ -1173,7 +1194,7 @@ class BluetoothPeripheralManager: NSObject {
bluetoothPeripheral.blePeripheral.parameterUpdateNeededAtNextConnect = true
}
case .WatlaaType, .DexcomG5Type, .BubbleType, .MiaoMiaoType, .BluconType, .GNSentryType, .BlueReaderType, .DropletType, .DexcomG4Type, .DexcomG6Type, .Libre2Type, .AtomType:
case .WatlaaType, .DexcomG5Type, .BubbleType, .MiaoMiaoType, .BluconType, .GNSentryType, .BlueReaderType, .DropletType, .DexcomG4Type, .DexcomG6Type, .Libre2Type, .AtomType, .DexcomG6FireflyType:
// nothing to check
break

View File

@ -37,3 +37,4 @@
"failed" = "Failed";
"calibrationNotNecessary" = "When using the Libre algoritm, it is not necessary to calibrate the sensor.";
"dexcomBatteryTooLow" = "The Transmitter battery is too low!";
"enterSensorCode" = "Enter Sensor Code";

View File

@ -160,4 +160,8 @@ enum Texts_HomeView {
return NSLocalizedString("dexcomBatteryTooLow", tableName: filename, bundle: Bundle.main, value: "The Transmitter battery is too low!", comment: "Error message in case Dexcom G5 (and G6?) battery is too low. This is deteced by wrong G5 values 2096896")
}()
static let enterSensorCode: String = {
return NSLocalizedString("enterSensorCode", tableName: filename, bundle: Bundle.main, value: "Enter Sensor Code", comment: "When user needs to enter sensor code, to start firefly sensor")
}()
}

View File

@ -363,8 +363,8 @@ class Trace {
}
case .DexcomG5Type:
if let dexcomG5 = blePeripheral.dexcomG5, !dexcomG5.isDexcomG6 {
case .DexcomG5Type, .DexcomG6Type, .DexcomG6FireflyType:
if let dexcomG5 = blePeripheral.dexcomG5 {
traceInfo.appendStringAndNewLine(" type = " + bluetoothPeripheralType.rawValue)
@ -374,17 +374,6 @@ class Trace {
}
case .DexcomG6Type:
if let dexcomG6 = blePeripheral.dexcomG5, dexcomG6.isDexcomG6 {
traceInfo.appendStringAndNewLine(" type = " + bluetoothPeripheralType.rawValue)
// if needed additional specific info can be added
traceInfo.appendStringAndNewLine(" voltageA : " + dexcomG6.voltageA.description)
traceInfo.appendStringAndNewLine(" voltageB : " + dexcomG6.voltageB.description)
}
case .BluconType:
if let blucon = blePeripheral.blucon {

View File

@ -19,7 +19,7 @@ class DexcomG5BluetoothPeripheralViewModel {
case firmWareVersion = 2
}
private enum ResetSettings:Int, CaseIterable {
/// should reset be done yes or no

View File

@ -0,0 +1,13 @@
import Foundation
import UIKit
import CoreBluetooth
class DexcomG6FireflyBluetoothPeripheralViewModel: DexcomG5BluetoothPeripheralViewModel {
// MARK: - overriden functions
override public func dexcomScreenTitle() -> String {
return BluetoothPeripheralType.DexcomG6FireflyType.rawValue
}
}

View File

@ -788,7 +788,7 @@ final class RootViewController: UIViewController {
trace("in cgmTransmitterInfoChanged, webOOPEnabled value changed to %{public}@, will stop the sensor", log: self.log, category: ConstantsLog.categoryRootView, type: .info, cgmTransmitter.isWebOOPEnabled().description)
self.stopSensor()
self.stopSensor(cGMTransmitter: cgmTransmitter)
}
@ -797,7 +797,7 @@ final class RootViewController: UIViewController {
trace("in cgmTransmitterInfoChanged, nonFixedSlopeEnabled value changed to %{public}@, will stop the sensor", log: self.log, category: ConstantsLog.categoryRootView, type: .info, cgmTransmitter.isNonFixedSlopeEnabled().description)
self.stopSensor()
self.stopSensor(cGMTransmitter: cgmTransmitter)
}
@ -813,7 +813,7 @@ final class RootViewController: UIViewController {
trace("in cgmTransmitterInfoChanged, sensorType value changed to %{public}@, will stop the sensor", log: self.log, category: ConstantsLog.categoryRootView, type: .info, cgmTransmitter.cgmTransmitterType().sensorType().rawValue)
self.stopSensor()
self.stopSensor(cGMTransmitter: cgmTransmitter)
}
@ -862,8 +862,8 @@ final class RootViewController: UIViewController {
/// 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 [GlucoseData], sensorTimeInMinutes: Int?) {
/// - sensorAge : 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], sensorAge: TimeInterval?) {
// unwrap calibrationsAccessor and coreDataManager and cgmTransmitter
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager, let cgmTransmitter = bluetoothPeripheralManager?.getCGMTransmitter() else {
@ -876,17 +876,10 @@ final class RootViewController: UIViewController {
if activeSensor == nil {
if let sensorTimeInMinutes = sensorTimeInMinutes, cgmTransmitter.cgmTransmitterType().canDetectNewSensor() {
if let sensorAge = sensorAge, cgmTransmitter.cgmTransmitterType().canDetectNewSensor() {
self.startSensor(cGMTransmitter: cgmTransmitter, sensorStarDate: Date(timeIntervalSinceNow: -sensorAge), sensorCode: nil, coreDataManager: coreDataManager)
activeSensor = Sensor(startDate: Date(timeInterval: -Double(sensorTimeInMinutes * 60), since: Date()),nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
if let activeSensor = activeSensor {
trace("created sensor with id : %{public}@ and startdate %{public}@", log: log, category: ConstantsLog.categoryRootView, type: .info, activeSensor.id, activeSensor.startDate.description)
} else {
trace("creation active sensor failed", log: log, category: ConstantsLog.categoryRootView, type: .info)
}
// save the newly created Sensor permenantly in coredata
coreDataManager.saveChanges()
}
}
@ -1179,8 +1172,10 @@ final class RootViewController: UIViewController {
changeButtonsStatusTo(enabled: UserDefaults.standard.isMaster)
guard let cgmTransmitter = self.bluetoothPeripheralManager?.getCGMTransmitter() else {break}
// no sensor needed in follower mode, stop it
stopSensor()
stopSensor(cGMTransmitter: cgmTransmitter)
case UserDefaults.Key.showReadingInNotification:
if !UserDefaults.standard.showReadingInNotification {
@ -1439,7 +1434,6 @@ final class RootViewController: UIViewController {
// assign deviceName, needed in the closure when creating alert. As closures can create strong references (to bluetoothTransmitter in this case), I'm fetching the deviceName here
let deviceName = bluetoothTransmitter.deviceName
// let alert = UIAlertController(title: "test title", message: "test message", keyboardType: .numberPad, text: nil, placeHolder: "...", actionTitle: nil, cancelTitle: nil, actionHandler: {_ in }, cancelHandler: nil)
let alert = UIAlertController(title: Texts_Calibrations.enterCalibrationValue, message: nil, keyboardType: UserDefaults.standard.bloodGlucoseUnitIsMgDl ? .numberPad:.decimalPad, text: nil, placeHolder: "...", actionTitle: nil, cancelTitle: nil, actionHandler: {
(text:String) in
@ -1464,7 +1458,13 @@ final class RootViewController: UIViewController {
trace("calibration : initial calibration, creating two calibrations", log: self.log, category: ConstantsLog.categoryRootView, type: .info)
// calling initialCalibration will create two calibrations, they are returned also but we don't need them
_ = calibrator.initialCalibration(firstCalibrationBgValue: valueAsDoubleConvertedToMgDl, firstCalibrationTimeStamp: Date(timeInterval: -(5*60), since: Date()), secondCalibrationBgValue: valueAsDoubleConvertedToMgDl, sensor: activeSensor, lastBgReadingsWithCalculatedValue0AndForSensor: &latestReadings, deviceName: deviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
let (_, calibration) = calibrator.initialCalibration(firstCalibrationBgValue: valueAsDoubleConvertedToMgDl, firstCalibrationTimeStamp: Date(timeInterval: -(5*60), since: Date()), secondCalibrationBgValue: valueAsDoubleConvertedToMgDl, sensor: activeSensor, lastBgReadingsWithCalculatedValue0AndForSensor: &latestReadings, deviceName: deviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// send calibration to transmitter (only used for Dexcom, if firefly flow is used)
if let calibration = calibration {
cgmTransmitter.calibrate(calibration: calibration)
}
} else {
@ -1473,8 +1473,11 @@ final class RootViewController: UIViewController {
trace("calibration : creating calibrations", log: self.log, category: ConstantsLog.categoryRootView, type: .info)
// calling createNewCalibration will create a new calibration, it is returned but we don't need it
_ = calibrator.createNewCalibration(bgValue: valueAsDoubleConvertedToMgDl, lastBgReading: latestReadings[0], sensor: activeSensor, lastCalibrationsForActiveSensorInLastXDays: &latestCalibrations, firstCalibration: firstCalibrationForActiveSensor, deviceName: deviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// create new calibration
let calibration = calibrator.createNewCalibration(bgValue: valueAsDoubleConvertedToMgDl, lastBgReading: latestReadings[0], sensor: activeSensor, lastCalibrationsForActiveSensorInLastXDays: &latestCalibrations, firstCalibration: firstCalibrationForActiveSensor, deviceName: deviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// send calibration to transmitter (only used for Dexcom, if firefly flow is used)
cgmTransmitter.calibrate(calibration: calibration)
}
@ -1524,20 +1527,13 @@ final class RootViewController: UIViewController {
switch cgmTransmitterType {
case .dexcomG4, .dexcomG5 :
case .dexcomG4, .dexcomG5, .dexcomG6 :
calibrator = DexcomCalibrator()
case .dexcomG6:
case .dexcomG6Firefly:
if (cgmTransmitter as! CGMG6Transmitter).isFireFly() {
calibrator = NoCalibrator()
} else {
calibrator = DexcomCalibrator()
}
calibrator = NoCalibrator()
case .miaomiao, .GNSentry, .Blucon, .Bubble, .Droplet1, .blueReader, .watlaa, .Libre2, .Atom:
@ -1846,6 +1842,10 @@ final class RootViewController: UIViewController {
/// when user clicks transmitter button, this will create and present the actionsheet, contents depend on type of transmitter and sensor status
private func createAndPresentSensorButtonActionSheet() {
// unwrap coredatamanager
guard let coreDataManager = coreDataManager else {return}
// initialize list of actions
var listOfActions = [UIAlertAction]()
@ -1858,18 +1858,35 @@ final class RootViewController: UIViewController {
// next action is to start or stop the sensor, can also be omitted depending on type of device - also not applicable for follower mode
if let cgmTransmitter = self.bluetoothPeripheralManager?.getCGMTransmitter() {
if cgmTransmitter.cgmTransmitterType().allowManualSensorStart() && UserDefaults.standard.isMaster {
// user needs to start and stop the sensor manually
// user can (or needs to) start and stop the sensor
var startStopAction: UIAlertAction
if activeSensor != nil {
startStopAction = UIAlertAction(title: Texts_HomeView.stopSensorActionTitle, style: .default) { (UIAlertAction) in
trace("in createAndPresentSensorButtonActionSheet, user clicked stop sensor, will stop the sensor", log: self.log, category: ConstantsLog.categoryRootView, type: .info)
self.stopSensor()
self.stopSensor(cGMTransmitter: cgmTransmitter)
}
} else {
startStopAction = UIAlertAction(title: Texts_HomeView.startSensorActionTitle, style: .default) { (UIAlertAction) in
self.startSensorAskUserForStarttime()
// either sensor needs a sensor start time, or a sensor code .. or none
if cgmTransmitter.cgmTransmitterType().needsSensorStartTime() {
self.startSensorAskUserForStarttime(cGMTransmitter: cgmTransmitter)
} else if cgmTransmitter.cgmTransmitterType().needsSensorStartCode() {
self.startSensorAskUserForSensorCode(cGMTransmitter: cgmTransmitter)
} else {
self.startSensor(cGMTransmitter: cgmTransmitter, sensorStarDate: Date(), sensorCode: nil, coreDataManager: coreDataManager)
}
}
}
@ -1941,36 +1958,17 @@ final class RootViewController: UIViewController {
}
/// 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()
}
// save the changes
coreDataManager?.saveChanges()
activeSensor = nil
// now that the activeSensor object has been destroyed, update (hide) the sensor countdown graphic
updateSensorCountdown()
}
/// start a new sensor, ask user for starttime
private func startSensorAskUserForStarttime() {
/// - parameters:
/// - cGMTransmitter is required because startSensor command will be sent also to the transmitter
private func startSensorAskUserForStarttime(cGMTransmitter: CGMTransmitter) {
// craete datePickerViewData
let datePickerViewData = DatePickerViewData(withMainTitle: Texts_HomeView.startSensorActionTitle, withSubTitle: nil, datePickerMode: .dateAndTime, date: Date(), minimumDate: nil, maximumDate: Date(), okButtonText: Texts_Common.Ok, cancelButtonText: Texts_Common.Cancel, onOkClick: {(date) in
if let coreDataManager = self.coreDataManager {
if let coreDataManager = self.coreDataManager, let cgmTransmitter = self.bluetoothPeripheralManager?.getCGMTransmitter() {
// set sensorStartTime
let sensorStartTime = date
self.activeSensor = Sensor(startDate: sensorStartTime, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// save the newly created Sensor permenantly in coredata
coreDataManager.saveChanges()
// start sensor with date chosen by user, sensorCode nil
self.startSensor(cGMTransmitter: cgmTransmitter, sensorStarDate: date, sensorCode: nil, coreDataManager: coreDataManager)
}
}, onCancelClick: nil)
@ -1996,6 +1994,27 @@ final class RootViewController: UIViewController {
}
/// start a new sensor, ask user for sensor code
/// - parameters:
/// - cGMTransmitter is required because startSensor command will be sent also to the transmitter
private func startSensorAskUserForSensorCode(cGMTransmitter: CGMTransmitter) {
let alert = UIAlertController(title: Texts_HomeView.enterSensorCode, message: nil, keyboardType:.numberPad, text: nil, placeHolder: "0000", actionTitle: nil, cancelTitle: nil, actionHandler: {
(text:String) in
if let coreDataManager = self.coreDataManager, let cgmTransmitter = self.bluetoothPeripheralManager?.getCGMTransmitter() {
// start sensor with date chosen by user, sensorCode nil
self.startSensor(cGMTransmitter: cgmTransmitter, sensorStarDate: Date(), sensorCode: text, coreDataManager: coreDataManager)
}
}, cancelHandler: nil)
self.present(alert, animated: true, completion: nil)
}
private func valueLabelLongPressed(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began {
@ -2646,6 +2665,52 @@ final class RootViewController: UIViewController {
}
}
/// - creates a new sensor and assigns it to activeSensor
/// - sends startSensor command to transmitter (ony useful for Firefly)
/// - saves to coredata
private func startSensor(cGMTransmitter: CGMTransmitter, sensorStarDate: Date, sensorCode: String?, coreDataManager: CoreDataManager) {
// create active sensor
let newSensor = Sensor(startDate: sensorStarDate, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// save the newly created Sensor permenantly in coredata
coreDataManager.saveChanges()
// send to transmitter
cGMTransmitter.startSensor(sensorCode: sensorCode, startDate: sensorStarDate)
// assign activeSensor to newSensor
activeSensor = newSensor
}
private func stopSensor(cGMTransmitter: CGMTransmitter) {
// create stopDate
let stopDate = Date()
// send stop sensor command to transmitter, don't check if there's an activeSensor in coredata or not, never know that there's a desync between coredata and transmitter
cGMTransmitter.stopSensor(stopDate: stopDate)
// no need to further continue if activeSensor = nil, and at the same time, unwrap coredataManager
guard let activeSensor = activeSensor, let coreDataManager = coreDataManager else {
return
}
// set endDate of activeSensor to stopDate
activeSensor.endDate = stopDate
// save changes to coreData
coreDataManager.saveChanges()
// asign nil to activeSensor
self.activeSensor = nil
// now that the activeSensor object has been destroyed, update (hide) the sensor countdown graphic
updateSensorCountdown()
}
}
@ -2653,10 +2718,23 @@ final class RootViewController: UIViewController {
/// conform to CGMTransmitterDelegate
extension RootViewController: CGMTransmitterDelegate {
func newSensorDetected() {
func newSensorDetected(sensorStartDate: Date?) {
trace("new sensor detected", log: log, category: ConstantsLog.categoryRootView, type: .info)
stopSensor()
// unwrap cgmTransmitter
guard let cgmTransmitter = self.bluetoothPeripheralManager?.getCGMTransmitter() else {return}
stopSensor(cGMTransmitter: cgmTransmitter)
// if sensorStartDate is given, then unwrap coreDataManager and startSensor
if let sensorStartDate = sensorStartDate, let coreDataManager = coreDataManager {
// use sensorCode nil, in the end there will be no start sensor command sent to the transmitter because we just received the sensorStartTime from the transmitter, so it's already started
startSensor(cGMTransmitter: cgmTransmitter, sensorStarDate: sensorStartDate, sensorCode: nil, coreDataManager: coreDataManager)
}
}
func sensorNotDetected() {
@ -2666,10 +2744,10 @@ extension RootViewController: CGMTransmitterDelegate {
}
func cgmTransmitterInfoReceived(glucoseData: inout [GlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorTimeInMinutes: Int?) {
func cgmTransmitterInfoReceived(glucoseData: inout [GlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorAge: TimeInterval?) {
trace("transmitterBatteryInfo %{public}@", log: log, category: ConstantsLog.categoryRootView, type: .debug, transmitterBatteryInfo?.description ?? "not received")
trace("sensor time in minutes %{public}@", log: log, category: ConstantsLog.categoryRootView, type: .debug, sensorTimeInMinutes?.description ?? "not received")
trace("sensor time in days %{public}@", log: log, category: ConstantsLog.categoryRootView, type: .debug, sensorAge?.days.round(toDecimalPlaces: 1).description ?? "not received")
trace("glucoseData size = %{public}@", log: log, category: ConstantsLog.categoryRootView, type: .info, glucoseData.count.description)
// if received transmitterBatteryInfo not nil, then store it
@ -2685,7 +2763,7 @@ extension RootViewController: CGMTransmitterDelegate {
}
// process new readings
processNewGlucoseData(glucoseData: &glucoseData, sensorTimeInMinutes: sensorTimeInMinutes)
processNewGlucoseData(glucoseData: &glucoseData, sensorAge: sensorAge)
}