commit
b33e8d2bbc
|
@ -7,6 +7,7 @@ Current Status
|
|||
- G5 and G6
|
||||
- MiaoMiao
|
||||
- G4 with xBridge
|
||||
- Blucon
|
||||
- Alerting
|
||||
- Upload to Nightscout
|
||||
- Follower mode, with NightScout
|
||||
|
@ -19,7 +20,4 @@ Not yet supported
|
|||
- sidiary
|
||||
- graph on the homescreen
|
||||
|
||||
Other restrictions
|
||||
- sensor change detection not fully working, you might have to restart the app after placing the new sensor, maybe two times. I need more testing for this (next sensor replacement)
|
||||
|
||||
For developers : please go to the Wiki : https://github.com/JohanDegraeve/xdripswift/wiki
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
F821CF9522ADB0D7005C1E43 /* HealthKitManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F821CF9422ADB0D7005C1E43 /* HealthKitManager.swift */; };
|
||||
F821CF9722AE589E005C1E43 /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F821CF9622AE589E005C1E43 /* HealthKit.framework */; };
|
||||
F821CF9D22AEF483005C1E43 /* BGReadingSpeaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = F821CF9C22AEF483005C1E43 /* BGReadingSpeaker.swift */; };
|
||||
F856CE5B22EDC8E50083E436 /* ConstantsBluetoothPairing.swift in Sources */ = {isa = PBXBuildFile; fileRef = F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */; };
|
||||
F85DC2ED21CFE2F500B9F74A /* BgReading+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2E721CFE2F500B9F74A /* BgReading+CoreDataProperties.swift */; };
|
||||
F85DC2EF21CFE2F500B9F74A /* Sensor+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2E921CFE2F500B9F74A /* Sensor+CoreDataProperties.swift */; };
|
||||
F85DC2F321CFE3D400B9F74A /* Calibration+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2F021CFE3D400B9F74A /* Calibration+CoreDataClass.swift */; };
|
||||
|
@ -55,6 +56,24 @@
|
|||
F867E2612252ADAB000FD265 /* Calibration+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F867E25D2252ADAB000FD265 /* Calibration+CoreDataProperties.swift */; };
|
||||
F897AAF92200F2D200CDDD10 /* CBPeripheralState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897AAF82200F2D200CDDD10 /* CBPeripheralState.swift */; };
|
||||
F897AAFB2201018800CDDD10 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897AAFA2201018800CDDD10 /* String.swift */; };
|
||||
F8A1584D22ECA445007F5B5D /* SettingsViewDevelopmentSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1584C22ECA445007F5B5D /* SettingsViewDevelopmentSettingsViewModel.swift */; };
|
||||
F8A1584F22ECB281007F5B5D /* SettingsViewInfoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1584E22ECB281007F5B5D /* SettingsViewInfoViewModel.swift */; };
|
||||
F8A1585122EDB597007F5B5D /* ConstantsBGGraphBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585022EDB597007F5B5D /* ConstantsBGGraphBuilder.swift */; };
|
||||
F8A1585322EDB602007F5B5D /* ConstantsBloodGlucose.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585222EDB602007F5B5D /* ConstantsBloodGlucose.swift */; };
|
||||
F8A1585522EDB706007F5B5D /* ConstantsCalibrationAlgorithms.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585422EDB706007F5B5D /* ConstantsCalibrationAlgorithms.swift */; };
|
||||
F8A1585722EDB754007F5B5D /* ConstantsCoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585622EDB754007F5B5D /* ConstantsCoreData.swift */; };
|
||||
F8A1585922EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585822EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift */; };
|
||||
F8A1585B22EDB7EA007F5B5D /* ConstantsDexcomG5.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585A22EDB7EA007F5B5D /* ConstantsDexcomG5.swift */; };
|
||||
F8A1585F22EDB81E007F5B5D /* ConstantsLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1585E22EDB81E007F5B5D /* ConstantsLog.swift */; };
|
||||
F8A1586122EDB844007F5B5D /* ConstantsNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586022EDB844007F5B5D /* ConstantsNotifications.swift */; };
|
||||
F8A1586322EDB86E007F5B5D /* ConstantsSounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586222EDB86E007F5B5D /* ConstantsSounds.swift */; };
|
||||
F8A1586522EDB89D007F5B5D /* ConstantsDefaultAlertTypeSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586422EDB89D007F5B5D /* ConstantsDefaultAlertTypeSettings.swift */; };
|
||||
F8A1586722EDB8BF007F5B5D /* ConstantsHomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586622EDB8BF007F5B5D /* ConstantsHomeView.swift */; };
|
||||
F8A1586B22EDB967007F5B5D /* ConstantsMaster.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586A22EDB967007F5B5D /* ConstantsMaster.swift */; };
|
||||
F8A1586D22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586C22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift */; };
|
||||
F8A1586F22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */; };
|
||||
F8A1587122EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */; };
|
||||
F8A1587322EDC893007F5B5D /* ConstantsDexcomShare.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */; };
|
||||
F8A54AAD22D6859200934E7A /* SlopeParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AAC22D6859200934E7A /* SlopeParameters.swift */; };
|
||||
F8A54AAF22D686CD00934E7A /* NightScoutBgReading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AAE22D686CD00934E7A /* NightScoutBgReading.swift */; };
|
||||
F8A54AB722D9111900934E7A /* CGMTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AB322D9111900934E7A /* CGMTransmitter.swift */; };
|
||||
|
@ -162,7 +181,6 @@
|
|||
F8BDD455221DEF22006EAB84 /* SettingsViews.strings in Resources */ = {isa = PBXBuildFile; fileRef = F8BDD457221DEF22006EAB84 /* SettingsViews.strings */; };
|
||||
F8E3C3AB21FE17B700907A04 /* StringProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E3C3AA21FE17B700907A04 /* StringProtocol.swift */; };
|
||||
F8E3C3AD21FE551C00907A04 /* DexcomCalibrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E3C3AC21FE551C00907A04 /* DexcomCalibrator.swift */; };
|
||||
F8EA6C7F21B70E390082976B /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EA6C7E21B70E390082976B /* Constants.swift */; };
|
||||
F8EA6C8221B723BC0082976B /* Date.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EA6C8121B723BC0082976B /* Date.swift */; };
|
||||
F8EA6CA921BBE3010082976B /* UniqueId.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EA6CA821BBE3010082976B /* UniqueId.swift */; };
|
||||
F8EA6CAD21BC2CA40082976B /* BluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EA6CAC21BC2CA40082976B /* BluetoothTransmitter.swift */; };
|
||||
|
@ -213,6 +231,7 @@
|
|||
F821CF9622AE589E005C1E43 /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
|
||||
F821CF9822AE589E005C1E43 /* xdrip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = xdrip.entitlements; sourceTree = "<group>"; };
|
||||
F821CF9C22AEF483005C1E43 /* BGReadingSpeaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BGReadingSpeaker.swift; sourceTree = "<group>"; };
|
||||
F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsBluetoothPairing.swift; sourceTree = "<group>"; };
|
||||
F85DC2E721CFE2F500B9F74A /* BgReading+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "BgReading+CoreDataProperties.swift"; path = "../Extensions/BgReading+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
F85DC2E921CFE2F500B9F74A /* Sensor+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Sensor+CoreDataProperties.swift"; path = "../Extensions/Sensor+CoreDataProperties.swift"; sourceTree = "<group>"; };
|
||||
F85DC2F021CFE3D400B9F74A /* Calibration+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Calibration+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||
|
@ -221,6 +240,24 @@
|
|||
F867E25D2252ADAB000FD265 /* Calibration+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Calibration+CoreDataProperties.swift"; path = "xdrip/Core Data/extensions/Calibration+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
|
||||
F897AAF82200F2D200CDDD10 /* CBPeripheralState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBPeripheralState.swift; sourceTree = "<group>"; };
|
||||
F897AAFA2201018800CDDD10 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
F8A1584C22ECA445007F5B5D /* SettingsViewDevelopmentSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewDevelopmentSettingsViewModel.swift; sourceTree = "<group>"; };
|
||||
F8A1584E22ECB281007F5B5D /* SettingsViewInfoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewInfoViewModel.swift; sourceTree = "<group>"; };
|
||||
F8A1585022EDB597007F5B5D /* ConstantsBGGraphBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsBGGraphBuilder.swift; sourceTree = "<group>"; };
|
||||
F8A1585222EDB602007F5B5D /* ConstantsBloodGlucose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsBloodGlucose.swift; sourceTree = "<group>"; };
|
||||
F8A1585422EDB706007F5B5D /* ConstantsCalibrationAlgorithms.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsCalibrationAlgorithms.swift; sourceTree = "<group>"; };
|
||||
F8A1585622EDB754007F5B5D /* ConstantsCoreData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsCoreData.swift; sourceTree = "<group>"; };
|
||||
F8A1585822EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsDefaultAlertLevels.swift; sourceTree = "<group>"; };
|
||||
F8A1585A22EDB7EA007F5B5D /* ConstantsDexcomG5.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsDexcomG5.swift; sourceTree = "<group>"; };
|
||||
F8A1585E22EDB81E007F5B5D /* ConstantsLog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsLog.swift; sourceTree = "<group>"; };
|
||||
F8A1586022EDB844007F5B5D /* ConstantsNotifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsNotifications.swift; sourceTree = "<group>"; };
|
||||
F8A1586222EDB86E007F5B5D /* ConstantsSounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSounds.swift; sourceTree = "<group>"; };
|
||||
F8A1586422EDB89D007F5B5D /* ConstantsDefaultAlertTypeSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsDefaultAlertTypeSettings.swift; sourceTree = "<group>"; };
|
||||
F8A1586622EDB8BF007F5B5D /* ConstantsHomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsHomeView.swift; sourceTree = "<group>"; };
|
||||
F8A1586A22EDB967007F5B5D /* ConstantsMaster.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsMaster.swift; sourceTree = "<group>"; };
|
||||
F8A1586C22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConstantsDexcomFollower.swift; sourceTree = "<group>"; };
|
||||
F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSuspensionPrevention.swift; sourceTree = "<group>"; };
|
||||
F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSpeakReadingLanguages.swift; sourceTree = "<group>"; };
|
||||
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsDexcomShare.swift; sourceTree = "<group>"; };
|
||||
F8A54AAC22D6859200934E7A /* SlopeParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlopeParameters.swift; sourceTree = "<group>"; };
|
||||
F8A54AAE22D686CD00934E7A /* NightScoutBgReading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightScoutBgReading.swift; sourceTree = "<group>"; };
|
||||
F8A54AB322D9111900934E7A /* CGMTransmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMTransmitter.swift; sourceTree = "<group>"; };
|
||||
|
@ -442,7 +479,6 @@
|
|||
F8BDD458221DEF24006EAB84 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/SettingsViews.strings; sourceTree = "<group>"; };
|
||||
F8E3C3AA21FE17B700907A04 /* StringProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringProtocol.swift; sourceTree = "<group>"; };
|
||||
F8E3C3AC21FE551C00907A04 /* DexcomCalibrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomCalibrator.swift; sourceTree = "<group>"; };
|
||||
F8EA6C7E21B70E390082976B /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
|
||||
F8EA6C8121B723BC0082976B /* Date.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Date.swift; sourceTree = "<group>"; };
|
||||
F8EA6CA821BBE3010082976B /* UniqueId.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniqueId.swift; sourceTree = "<group>"; };
|
||||
F8EA6CAC21BC2CA40082976B /* BluetoothTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTransmitter.swift; sourceTree = "<group>"; };
|
||||
|
@ -976,6 +1012,8 @@
|
|||
F8B3A83C227F090D004BA588 /* SettingsViewHealthKitSettingsViewModel.swift */,
|
||||
F8B3A83D227F090D004BA588 /* SettingsViewSpeakSettingsViewModel.swift */,
|
||||
F8B3A83E227F090D004BA588 /* SettingsViewGeneralSettingsViewModel.swift */,
|
||||
F8A1584C22ECA445007F5B5D /* SettingsViewDevelopmentSettingsViewModel.swift */,
|
||||
F8A1584E22ECB281007F5B5D /* SettingsViewInfoViewModel.swift */,
|
||||
);
|
||||
path = SettingsViewModels;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1036,7 +1074,23 @@
|
|||
F8EA6C7D21B70DEA0082976B /* Constants */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F8EA6C7E21B70E390082976B /* Constants.swift */,
|
||||
F8A1585022EDB597007F5B5D /* ConstantsBGGraphBuilder.swift */,
|
||||
F8A1585222EDB602007F5B5D /* ConstantsBloodGlucose.swift */,
|
||||
F8A1585422EDB706007F5B5D /* ConstantsCalibrationAlgorithms.swift */,
|
||||
F8A1585622EDB754007F5B5D /* ConstantsCoreData.swift */,
|
||||
F8A1585822EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift */,
|
||||
F8A1586422EDB89D007F5B5D /* ConstantsDefaultAlertTypeSettings.swift */,
|
||||
F8A1586C22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift */,
|
||||
F8A1585A22EDB7EA007F5B5D /* ConstantsDexcomG5.swift */,
|
||||
F8A1586622EDB8BF007F5B5D /* ConstantsHomeView.swift */,
|
||||
F8A1585E22EDB81E007F5B5D /* ConstantsLog.swift */,
|
||||
F8A1586A22EDB967007F5B5D /* ConstantsMaster.swift */,
|
||||
F8A1586022EDB844007F5B5D /* ConstantsNotifications.swift */,
|
||||
F8A1586222EDB86E007F5B5D /* ConstantsSounds.swift */,
|
||||
F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */,
|
||||
F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */,
|
||||
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */,
|
||||
F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */,
|
||||
);
|
||||
name = Constants;
|
||||
path = xdrip/Constants;
|
||||
|
@ -1117,7 +1171,7 @@
|
|||
TargetAttributes = {
|
||||
F8AC425921ADEBD60078C348 = {
|
||||
CreatedOnToolsVersion = 10.1;
|
||||
LastSwiftMigration = 1010;
|
||||
LastSwiftMigration = 1030;
|
||||
SystemCapabilities = {
|
||||
com.apple.BackgroundModes = {
|
||||
enabled = 1;
|
||||
|
@ -1265,6 +1319,7 @@
|
|||
F8B3A81B227DEC92004BA588 /* SensorsAccessor.swift in Sources */,
|
||||
F8B3A85D22821BB6004BA588 /* Int.swift in Sources */,
|
||||
F8A54AAF22D686CD00934E7A /* NightScoutBgReading.swift in Sources */,
|
||||
F8A1585722EDB754007F5B5D /* ConstantsCoreData.swift in Sources */,
|
||||
F821CF9022AB1068005C1E43 /* DatePickerViewData.swift in Sources */,
|
||||
F8025E4E21ED450300ECF0C0 /* Double.swift in Sources */,
|
||||
F8B3A853227F2743004BA588 /* AlertsSettingsViewController.swift in Sources */,
|
||||
|
@ -1286,15 +1341,19 @@
|
|||
F8AC42A121B31F170078C348 /* xdrip.xcdatamodeld in Sources */,
|
||||
F8A54ADB22D911BA00934E7A /* AuthRequestTxMessage.swift in Sources */,
|
||||
F8EA6CA921BBE3010082976B /* UniqueId.swift in Sources */,
|
||||
F8A1585122EDB597007F5B5D /* ConstantsBGGraphBuilder.swift in Sources */,
|
||||
F81D6D4822BD5F62005EFAE2 /* DexcomShareUploadManager.swift in Sources */,
|
||||
F8A7406E22D9C0E700967CFC /* CGMBluconTransmitter.swift in Sources */,
|
||||
F8A1586B22EDB967007F5B5D /* ConstantsMaster.swift in Sources */,
|
||||
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
|
||||
F8A54B0022D9179100934E7A /* ParseLibreData.swift in Sources */,
|
||||
F8025E5421EE8D2100ECF0C0 /* Libre1Calibrator.swift in Sources */,
|
||||
F81FA00A228F53680028C70F /* TextsHomeView.swift in Sources */,
|
||||
F8E3C3AD21FE551C00907A04 /* DexcomCalibrator.swift in Sources */,
|
||||
F821CF61229BF4A2005C1E43 /* NightScoutUploadManager.swift in Sources */,
|
||||
F8A1587122EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift in Sources */,
|
||||
F8A54ADD22D911BA00934E7A /* BatteryStatusTxMessage.swift in Sources */,
|
||||
F8A1585522EDB706007F5B5D /* ConstantsCalibrationAlgorithms.swift in Sources */,
|
||||
F8A54ADA22D911BA00934E7A /* TransmitterMessage.swift in Sources */,
|
||||
F897AAF92200F2D200CDDD10 /* CBPeripheralState.swift in Sources */,
|
||||
F8A54AE522D911BA00934E7A /* TransmitterVersionRxMessage.swift in Sources */,
|
||||
|
@ -1314,25 +1373,34 @@
|
|||
F85DC2F421CFE3D400B9F74A /* Sensor+CoreDataClass.swift in Sources */,
|
||||
F8B3A844227F090E004BA588 /* SettingsViewAlertSettingsViewModel.swift in Sources */,
|
||||
F8A54AD822D911BA00934E7A /* CGMG5Transmitter.swift in Sources */,
|
||||
F8A1586322EDB86E007F5B5D /* ConstantsSounds.swift in Sources */,
|
||||
F8A1587322EDC893007F5B5D /* ConstantsDexcomShare.swift in Sources */,
|
||||
F8A1586F22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift in Sources */,
|
||||
F8A54AB822D9111900934E7A /* TransmitterBatteryInfo.swift in Sources */,
|
||||
F8B3A82D227F07D6004BA588 /* SettingsNavigationController.swift in Sources */,
|
||||
F8A54AB722D9111900934E7A /* CGMTransmitter.swift in Sources */,
|
||||
F8B3A830227F085A004BA588 /* SettingsTableViewCell.swift in Sources */,
|
||||
F8A1586122EDB844007F5B5D /* ConstantsNotifications.swift in Sources */,
|
||||
F8B3A81C227DEC92004BA588 /* AlertEntriesAccessor.swift in Sources */,
|
||||
F8A1585B22EDB7EA007F5B5D /* ConstantsDexcomG5.swift in Sources */,
|
||||
F8BDD452221DEAB2006EAB84 /* TextsSettingsView.swift in Sources */,
|
||||
F897AAFB2201018800CDDD10 /* String.swift in Sources */,
|
||||
F8B3A847227F090E004BA588 /* SettingsViewNightScoutSettingsViewModel.swift in Sources */,
|
||||
F8B3A79622635A25004BA588 /* AlertEntry+CoreDataClass.swift in Sources */,
|
||||
F8AC425E21ADEBD60078C348 /* AppDelegate.swift in Sources */,
|
||||
F821CF8E22AB090C005C1E43 /* DatePickerViewController.swift in Sources */,
|
||||
F8A1585322EDB602007F5B5D /* ConstantsBloodGlucose.swift in Sources */,
|
||||
F8A54AE322D911BA00934E7A /* AuthRequestRxMessage.swift in Sources */,
|
||||
F8A54AD722D911BA00934E7A /* CGMG6Transmitter.swift in Sources */,
|
||||
F81F9FF822861E6D0028C70F /* KeyValueObserverTimeKeeper.swift in Sources */,
|
||||
F8B3A858227F6971004BA588 /* UISwitch.swift in Sources */,
|
||||
F8A1586722EDB8BF007F5B5D /* ConstantsHomeView.swift in Sources */,
|
||||
F8A1585922EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift in Sources */,
|
||||
F8A54AAD22D6859200934E7A /* SlopeParameters.swift in Sources */,
|
||||
F8B3A783225D37F2004BA588 /* TextsNightScoutTestResult.swift in Sources */,
|
||||
F8025C0A21D94FD700ECF0C0 /* CBManagerState.swift in Sources */,
|
||||
F8B3A80A227A3D11004BA588 /* TextsAlertTypeSettings.swift in Sources */,
|
||||
F8A1586D22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift in Sources */,
|
||||
F8B3A850227F26F8004BA588 /* AlertTypesSettingsViewController.swift in Sources */,
|
||||
F8A7407022DBB24800967CFC /* BluconTransmitterOpCode.swift in Sources */,
|
||||
F8EA6CAD21BC2CA40082976B /* BluetoothTransmitter.swift in Sources */,
|
||||
|
@ -1345,6 +1413,7 @@
|
|||
F81F9FFC2288C7530028C70F /* NewAlertSettingsViewController.swift in Sources */,
|
||||
F81FA0002289E4990028C70F /* AlertSettingsViewControllerData.swift in Sources */,
|
||||
F8A7407222DCDA3E00967CFC /* BluconUtilities.swift in Sources */,
|
||||
F856CE5B22EDC8E50083E436 /* ConstantsBluetoothPairing.swift in Sources */,
|
||||
F8B48A9422B2A705009BCC01 /* TextsSpeakReading.swift in Sources */,
|
||||
F8A54AE222D911BA00934E7A /* ResetMessage.swift in Sources */,
|
||||
F821CF5F229BF43A005C1E43 /* ApplicationManager.swift in Sources */,
|
||||
|
@ -1367,16 +1436,19 @@
|
|||
F8025C1121DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift in Sources */,
|
||||
F8A54AD922D911BA00934E7A /* TransmitterVersionTxMessage.swift in Sources */,
|
||||
F821CF5E229BF43A005C1E43 /* BgReading+NightScout.swift in Sources */,
|
||||
F8EA6C7F21B70E390082976B /* Constants.swift in Sources */,
|
||||
F8A54ADE22D911BA00934E7A /* AESCrypt.m in Sources */,
|
||||
F8A54ADC22D911BA00934E7A /* AuthChallengeTxMessage.swift in Sources */,
|
||||
F8025C1321DA683400ECF0C0 /* Data.swift in Sources */,
|
||||
F85DC2EF21CFE2F500B9F74A /* Sensor+CoreDataProperties.swift in Sources */,
|
||||
F8A54AFA22D9156600934E7A /* CGMMiaoMiaoTransmitter.swift in Sources */,
|
||||
F8A1584D22ECA445007F5B5D /* SettingsViewDevelopmentSettingsViewModel.swift in Sources */,
|
||||
F8A54AB922D9111900934E7A /* CGMTransmitterDelegate.swift in Sources */,
|
||||
F8B3A856227F28DC004BA588 /* AlertTypeSettingsViewController.swift in Sources */,
|
||||
F8A54AE822D911BA00934E7A /* BatteryStatusRxMessage.swift in Sources */,
|
||||
F8A1584F22ECB281007F5B5D /* SettingsViewInfoViewModel.swift in Sources */,
|
||||
F8B3A845227F090E004BA588 /* SettingsViewDexcomSettingsViewModel.swift in Sources */,
|
||||
F8A1585F22EDB81E007F5B5D /* ConstantsLog.swift in Sources */,
|
||||
F8A1586522EDB89D007F5B5D /* ConstantsDefaultAlertTypeSettings.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -1730,7 +1802,7 @@
|
|||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "xdrip/xdrip-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Debug;
|
||||
|
@ -1753,7 +1825,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = net.johandegraeve.iosxdripreader;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "xdrip/xdrip-Bridging-Header.h";
|
||||
SWIFT_VERSION = 4.2;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = 1;
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
@ -495,10 +495,10 @@ extension Calibrator {
|
|||
/// - bgReading : reading that needs to be updated - inout parameter to improve performance
|
||||
private func updateCalculatedValue(for bgReading:BgReading) {
|
||||
if (bgReading.calculatedValue < 10) {
|
||||
bgReading.calculatedValue = Constants.CalibrationAlgorithms.bgReadingErrorValue
|
||||
bgReading.calculatedValue = ConstantsCalibrationAlgorithms.bgReadingErrorValue
|
||||
bgReading.hideSlope = true
|
||||
} else {
|
||||
bgReading.calculatedValue = min(Constants.CalibrationAlgorithms.maximumBgReadingCalculatedValue, max(Constants.CalibrationAlgorithms.minimumBgReadingCalculatedValue, bgReading.calculatedValue))
|
||||
bgReading.calculatedValue = min(ConstantsCalibrationAlgorithms.maximumBgReadingCalculatedValue, max(ConstantsCalibrationAlgorithms.minimumBgReadingCalculatedValue, bgReading.calculatedValue))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -517,11 +517,11 @@ extension Calibrator {
|
|||
/// - withSensor : the currently active sensor, optional
|
||||
private func calculateAgeAdjustedRawValue(for bgReading:BgReading, withSensor sensor:Sensor?) {
|
||||
if let sensor = sensor {
|
||||
let adjustfor:Double = Constants.CalibrationAlgorithms.ageAdjustmentTime - (bgReading.timeStamp.toMillisecondsAsDouble() - sensor.startDate.toMillisecondsAsDouble())
|
||||
let adjustfor:Double = ConstantsCalibrationAlgorithms.ageAdjustmentTime - (bgReading.timeStamp.toMillisecondsAsDouble() - sensor.startDate.toMillisecondsAsDouble())
|
||||
if (adjustfor <= 0 || !ageAdjustMentNeeded) {
|
||||
bgReading.ageAdjustedRawValue = bgReading.rawData
|
||||
} else {
|
||||
bgReading.ageAdjustedRawValue = ((Constants.CalibrationAlgorithms.ageAdjustmentFactor * (adjustfor / Constants.CalibrationAlgorithms.ageAdjustmentTime)) * bgReading.rawData) + bgReading.rawData
|
||||
bgReading.ageAdjustedRawValue = ((ConstantsCalibrationAlgorithms.ageAdjustmentFactor * (adjustfor / ConstantsCalibrationAlgorithms.ageAdjustmentTime)) * bgReading.rawData) + bgReading.rawData
|
||||
}
|
||||
} else {
|
||||
bgReading.ageAdjustedRawValue = bgReading.rawData
|
||||
|
|
|
@ -1,392 +0,0 @@
|
|||
//Application level constants
|
||||
import Foundation
|
||||
|
||||
struct Constants {
|
||||
|
||||
enum BGGraphBuilder {
|
||||
static let maxSlopeInMinutes = 21
|
||||
static let defaultLowMarkInMgdl = 70.0
|
||||
static let defaultHighMmarkInMgdl = 170.0
|
||||
}
|
||||
|
||||
enum BloodGlucose {
|
||||
static let mmollToMgdl = 18.01801801801802
|
||||
static let mgDlToMmoll = 0.0555
|
||||
static let libreMultiplier = 117.64705
|
||||
}
|
||||
|
||||
/// constants used in calibration algorithm
|
||||
enum CalibrationAlgorithms {
|
||||
// age adjustment constants, only for non Libre
|
||||
static let ageAdjustmentTime = 86400000 * 1.9
|
||||
static let ageAdjustmentFactor = 0.45
|
||||
|
||||
// minimum and maxium values for a reading
|
||||
static let minimumBgReadingCalculatedValue = 39.0
|
||||
static let maximumBgReadingCalculatedValue = 400.0
|
||||
static let bgReadingErrorValue = 38.0
|
||||
}
|
||||
|
||||
/// coredata specific constants
|
||||
enum CoreData {
|
||||
static let modelName = "xdrip"
|
||||
}
|
||||
|
||||
/// default alert levels to be used when creating defalt alert entries
|
||||
enum DefaultAlertLevels {
|
||||
// default battery alert level, below this level an alert should be generated - this default value will be used when changing transmittertype
|
||||
static let defaultBatteryAlertLevelDexcomG5 = 300
|
||||
static let defaultBatteryAlertLevelDexcomG4 = 210
|
||||
static let defaultBatteryAlertLevelMiaoMiao = 20
|
||||
static let defaultBatteryAlertLevelGNSEntry = 20
|
||||
static let defaultBatteryAlertLevelBlucon = 20
|
||||
|
||||
// blood glucose level alert values in mgdl
|
||||
static let veryHigh = 250
|
||||
static let veryLow = 50
|
||||
static let high = 170
|
||||
static let low = 70
|
||||
|
||||
// in minutes, after how many minutes of now reading should alert be raised
|
||||
static let missedReading = 30
|
||||
|
||||
// in hours, after how many hours alert to request a new calibration
|
||||
static let calibration = 24
|
||||
}
|
||||
|
||||
/// dexcom G5 specific constants
|
||||
enum DexcomG5 {
|
||||
/// how often to read battery level
|
||||
static let batteryReadPeriodInHours = 12.0
|
||||
|
||||
/// in case transmitter needs pairing, how long to keep connection up to give time to the user to accept the pairing request, inclusive opening the notification
|
||||
static let maxTimeToAcceptPairingInSeconds = 60
|
||||
}
|
||||
|
||||
/// for use in OSLog
|
||||
enum Log {
|
||||
/// for use in OSLog
|
||||
static let subSystem = "xDrip"
|
||||
/// for use in OSLog
|
||||
static let categoryBlueTooth = "bluetooth"
|
||||
/// for use in cgm transmitter miaomiao
|
||||
static let categoryCGMMiaoMiao = "cgmmiaomiao"
|
||||
/// for use in cgm xdripg4
|
||||
static let categoryCGMxDripG4 = "cgmxdripg4"
|
||||
/// for use in firstview
|
||||
static let categoryFirstView = "firstview"
|
||||
/// calibration
|
||||
static let calibration = "Calibration"
|
||||
/// debuglogging
|
||||
static let debuglogging = "xdripdebuglogging"
|
||||
/// G5
|
||||
static let categoryCGMG5 = "categoryCGMG5"
|
||||
/// GNSEntry
|
||||
static let categoryCGMGNSEntry = "categoryCGMGNSEntry"
|
||||
/// Blucon
|
||||
static let categoryBlucon = "categoryBlucon"
|
||||
/// core data manager
|
||||
static let categoryCoreDataManager = "categoryCoreDataManager"
|
||||
/// application data bgreadings
|
||||
static let categoryApplicationDataBgReadings = "categoryApplicationDataBgReadings"
|
||||
/// application data calibrations
|
||||
static let categoryApplicationDataCalibrations = "categoryApplicationDataCalibrations"
|
||||
/// application data sensors
|
||||
static let categoryApplicationDataSensors = "categoryApplicationDataSensors"
|
||||
/// application data alerttypes
|
||||
static let categoryApplicationDataAlertTypes = "categoryApplicationDataAlertTypes"
|
||||
/// application data alertentries
|
||||
static let categoryApplicationDataAlertEntries = "categoryApplicationDataAlertEntries"
|
||||
/// nightscout uploader
|
||||
static let categoryNightScoutUploadManager = "categoryNightScoutUploadManager"
|
||||
/// nightscout follow
|
||||
static let categoryNightScoutFollowManager = "categoryNightScoutFollowManager"
|
||||
/// alertmanager
|
||||
static let categoryAlertManager = "categoryAlertManager"
|
||||
/// playsound
|
||||
static let categoryPlaySound = "categoryPlaySound"
|
||||
/// healthkit manager
|
||||
static let categoryHealthKitManager = "categoryHealthKitManager"
|
||||
/// SettingsViewHealthKitSettingsViewModel
|
||||
static let categorySettingsViewHealthKitSettingsViewModel = "categorySettingsViewHealthKitSettingsViewModel"
|
||||
/// dexcom share upload manager
|
||||
static let categoryDexcomShareUploadManager = "categoryDexcomShareUploadManager"
|
||||
}
|
||||
|
||||
enum Notifications {
|
||||
|
||||
/// identifiers for alert categories
|
||||
enum CategoryIdentifiersForAlerts {
|
||||
/// for initial calibration
|
||||
static let initialCalibrationRequest = "InititalCalibrationRequest"
|
||||
/// subsequent calibration request
|
||||
static let subsequentCalibrationRequest = "SubsequentCalibrationRequest"
|
||||
/// high alert
|
||||
static let highAlert = "highAlert"
|
||||
/// low alert
|
||||
static let lowAlert = "lowAlert"
|
||||
/// very high alert
|
||||
static let veryHighAlert = "veryHighAlert"
|
||||
/// very low alert
|
||||
static let veryLowAlert = "veryLowAlert"
|
||||
/// missed reading alert
|
||||
static let missedReadingAlert = "missedReadingAlert"
|
||||
/// battery low
|
||||
static let batteryLow = "batteryLow"
|
||||
}
|
||||
|
||||
/// identifiers for alert notifications
|
||||
enum NotificationIdentifiersForAlerts {
|
||||
/// high alert
|
||||
static let highAlert = "highAlert"
|
||||
/// low alert
|
||||
static let lowAlert = "lowAlert"
|
||||
/// very high alert
|
||||
static let veryHighAlert = "veryHighAlert"
|
||||
/// very low alert
|
||||
static let veryLowAlert = "veryLowAlert"
|
||||
/// missed reading alert
|
||||
static let missedReadingAlert = "missedReadingAlert"
|
||||
/// battery low
|
||||
static let batteryLow = "batteryLow"
|
||||
}
|
||||
|
||||
/// identifiers for calibration requests
|
||||
enum NotificationIdentifiersForCalibration {
|
||||
/// for initial calibration
|
||||
static let initialCalibrationRequest = "initialCalibrationRequest"
|
||||
/// subsequent calibration request
|
||||
static let subsequentCalibrationRequest = "subsequentCalibrationRequest"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForBgReading {
|
||||
/// bgreading notification
|
||||
static let bgReadingNotificationRequest = "bgReadingNotificationRequest"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForSensorNotDetected {
|
||||
/// sensor not detected notification
|
||||
static let sensorNotDetected = "sensorNotDetected"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForTransmitterNeedsPairing {
|
||||
/// transmitter needs pairing
|
||||
static let transmitterNeedsPairing = "transmitterNeedsPairing"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForResetResult {
|
||||
/// transmitter reset result
|
||||
static let transmitterResetResult = "transmitterResetResult"
|
||||
}
|
||||
}
|
||||
|
||||
/// defines name of the Soundfile and name of the sound shown to the user with an extra function - both are defined in one case, seperated by a backslash - to be used for alerts - all these sounds will be shown
|
||||
enum Sounds: String, CaseIterable {
|
||||
|
||||
// here using case iso properties because we want to iterate through them
|
||||
/// name of the sound as shown to the user, and also stored in the alerttype
|
||||
case batterwakeup = "Better Wake Up/betterwakeup.mp3"
|
||||
case bruteforce = "Brute Force/bruteforce.mp3"
|
||||
case modernalarm2 = "Modern Alert 2/modern2.mp3"
|
||||
case modernalarm = "Modern Alert/modernalarm.mp3"
|
||||
case shorthigh1 = "Short High 1/shorthigh1.mp3"
|
||||
case shorthigh2 = "Short High 2/shorthigh2.mp3"
|
||||
case shorthigh3 = "Short High 3/shorthigh3.mp3"
|
||||
case shorthigh4 = "Short High 4/shorthigh4.mp3"
|
||||
case shortlow1 = "Short Low 1/shortlow1.mp3"
|
||||
case shortlow2 = "Short Low 2/shortlow2.mp3"
|
||||
case shortlow3 = "Short Low 3/shortlow3.mp3"
|
||||
case shortlow4 = "Short Low 4/shortlow4.mp3"
|
||||
case spaceship = "Space Ship/spaceship.mp3"
|
||||
case xdripalert = "xDrip Alert/xdripalert.aif"
|
||||
|
||||
/// gets all sound names in array, ie part of the case before the /
|
||||
static func allSoundsBySoundNameAndFileName() -> (soundNames:[String], fileNames:[String]) {
|
||||
var soundNames = [String]()
|
||||
var soundFileNames = [String]()
|
||||
|
||||
soundloop: for sound in Constants.Sounds.allCases {
|
||||
|
||||
// Constants.Sounds defines available sounds. Per case there a string which is the soundname as shown in the UI and the filename of the sound in the Resources folder, seperated by backslash
|
||||
// get array of indexes, of location of "/"
|
||||
let indexOfBackSlash = sound.rawValue.indexes(of: "/")
|
||||
|
||||
// define range to get the soundname (as shown in UI)
|
||||
let soundNameRange = sound.rawValue.startIndex..<indexOfBackSlash[0]
|
||||
|
||||
// now get the soundName in a string
|
||||
let soundName = String(sound.rawValue[soundNameRange])
|
||||
|
||||
// add the soundName to the returnvalue
|
||||
soundNames.append(soundName)
|
||||
|
||||
// define range to get the soundFileName
|
||||
let languageCodeRange = sound.rawValue.index(after: indexOfBackSlash[0])..<sound.rawValue.endIndex
|
||||
|
||||
// now get the language in a string
|
||||
let fileName = String(sound.rawValue[languageCodeRange])
|
||||
// add the languageCode to the returnvalue
|
||||
|
||||
soundFileNames.append(fileName)
|
||||
|
||||
}
|
||||
return (soundNames, soundFileNames)
|
||||
}
|
||||
|
||||
/// gets the soundname for specific case
|
||||
static func getSoundName(forSound:Sounds) -> String {
|
||||
let indexOfBackSlash = forSound.rawValue.indexes(of: "/")
|
||||
let soundNameRange = forSound.rawValue.startIndex..<indexOfBackSlash[0]
|
||||
return String(forSound.rawValue[soundNameRange])
|
||||
}
|
||||
|
||||
/// gets the soundFie for specific case
|
||||
static func getSoundFile(forSound:Sounds) -> String {
|
||||
let indexOfBackSlash = forSound.rawValue.indexes(of: "/")
|
||||
let soundNameRange = forSound.rawValue.index(after: indexOfBackSlash[0])..<forSound.rawValue.endIndex
|
||||
return String(forSound.rawValue[soundNameRange])
|
||||
}
|
||||
}
|
||||
|
||||
/// default values to be used when creating a new AlertType
|
||||
enum DefaultAlertTypeSettings {
|
||||
|
||||
static let enabled = true
|
||||
static let name = Texts_Common.default0
|
||||
static let overrideMute = false
|
||||
static let snooze = true
|
||||
static let snoozePeriod = Int16(60)
|
||||
static let vibrate = true
|
||||
static let soundName:String? = nil
|
||||
}
|
||||
|
||||
/// constants for home view, ie first view
|
||||
enum HomeView {
|
||||
|
||||
/// how often to update the labels in the homeview (ie label with latest reading, minutes ago, etc..)
|
||||
static let updateHomeViewIntervalInSeconds = 15.0
|
||||
|
||||
/// info email adres, appears in licenseInfo
|
||||
static let infoEmailAddress = "xdrip@proximus.be"
|
||||
|
||||
/// application name, appears in licenseInfo as title
|
||||
static let applicationName = "xDrip"
|
||||
}
|
||||
|
||||
/// constants for follower mode
|
||||
enum Follower {
|
||||
|
||||
/// maximum days of readings to download
|
||||
static let maxiumDaysOfReadingsToDownload = 1
|
||||
|
||||
/// maximum age in seconds, of reading in alert flow. If age of latest reading is more than this number, then no alert check will be done
|
||||
static let maximumBgReadingAgeForAlertsInSeconds = 240.0
|
||||
}
|
||||
|
||||
/// constants typically for master mode
|
||||
enum Master {
|
||||
|
||||
/// maximum age in seconds, of reading in alert flow. If age of latest reading is more than this number, then no alert check will be done
|
||||
static let maximumBgReadingAgeForAlertsInSeconds = 60.0
|
||||
}
|
||||
|
||||
/// suspension prevention
|
||||
enum SuspensionPrevention {
|
||||
|
||||
/// name of the file that has the sound to play
|
||||
static let soundFileName = "1-millisecond-of-silence.mp3"//20ms-of-silence.caf"
|
||||
|
||||
/// how often to play the sound, in seconds
|
||||
static let interval = 5
|
||||
}
|
||||
|
||||
/// supported languages for speak readings - defines name and language code, example "Dutch" and "nl-NL", both are defined in one case, seperated by a backslash
|
||||
///
|
||||
/// alphabetically ordered
|
||||
enum SpeakReadingLanguages: String, CaseIterable {
|
||||
|
||||
case chinese = "Chinese/zh"
|
||||
case dutch = "Dutch/nl"
|
||||
case english = "English/en"
|
||||
case french = "French/fr"
|
||||
case italian = "Italian/it"
|
||||
case polish = "Polish/pl-PL"
|
||||
case portugese_portugal = "Portuguese/pt"
|
||||
case portugese_brasil = "Portuguese (Brazil)/pt-BR"
|
||||
case russian = "Russian/ru"
|
||||
case slovenian = "Slovenian/sl"
|
||||
case spanish_mexico = "Spanish (Mexico)/es-MX"
|
||||
case spanish_spain = "Spanish (Spain)/es-ES"
|
||||
|
||||
/// gets all language names and language codes in two arrays
|
||||
/// - returns:
|
||||
/// ie part of the case before the / in the first array, part of the case after the / in the second array
|
||||
public static var allLanguageNamesAndCodes: (names:[String], codes:[String]) {
|
||||
var languageNames = [String]()
|
||||
var languageCodes = [String]()
|
||||
|
||||
languageloop: for speakReadingLanguage in Constants.SpeakReadingLanguages.allCases {
|
||||
|
||||
// SpeakReadingLanguages defines available languages. Per case there is a string which is the language as shown in the UI and the language code, seperated by backslash
|
||||
// get array of indexes, of location of "/"
|
||||
let indexOfBackSlash = speakReadingLanguage.rawValue.indexes(of: "/")
|
||||
|
||||
// define range to get the language (as shown in UI)
|
||||
let languageNameRange = speakReadingLanguage.rawValue.startIndex..<indexOfBackSlash[0]
|
||||
|
||||
// now get the language in a string
|
||||
let language = String(speakReadingLanguage.rawValue[languageNameRange])
|
||||
|
||||
// add the soundName to the returnvalue
|
||||
languageNames.append(language)
|
||||
|
||||
// define range to get the languagecode
|
||||
let languageCodeRange = speakReadingLanguage.rawValue.index(after: indexOfBackSlash[0])..<speakReadingLanguage.rawValue.endIndex
|
||||
|
||||
// now get the language in a string
|
||||
let languageCode = String(speakReadingLanguage.rawValue[languageCodeRange])
|
||||
// add the languageCode to the returnvalue
|
||||
|
||||
languageCodes.append(languageCode)
|
||||
|
||||
}
|
||||
return (languageNames, languageCodes)
|
||||
}
|
||||
|
||||
/// gets the language name for specific case
|
||||
static func languageName(forLanguageCode:String?) -> String {
|
||||
|
||||
if let forLanguageCode = forLanguageCode {
|
||||
for (index, languageCode) in allLanguageNamesAndCodes.codes.enumerated() {
|
||||
if languageCode == forLanguageCode {
|
||||
return allLanguageNamesAndCodes.names[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
return Texts_SpeakReading.defaultLanguageCode
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// constants for Dexcom Share
|
||||
enum DexcomShare {
|
||||
|
||||
/// applicationId to use in Dexcom Share protocol
|
||||
static let applicationId = "d8665ade-9673-4e27-9ff6-92db4ce13d13"
|
||||
|
||||
/// us share base url
|
||||
static let usBaseShareUrl = "https://share2.dexcom.com/ShareWebServices/Services"
|
||||
|
||||
/// non us share base url
|
||||
static let nonUsBaseShareUrl = "https://shareous1.dexcom.com/ShareWebServices/Services"
|
||||
|
||||
}
|
||||
|
||||
/// constants related to Bluetooth pairing
|
||||
enum BluetoothPairing {
|
||||
|
||||
/// minimum time in seconds between two pairing notifications
|
||||
static let minimumTimeBetweenTwoPairingNotificationsInSeconds = 30
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
enum ConstantsBGGraphBuilder {
|
||||
static let maxSlopeInMinutes = 21
|
||||
static let defaultLowMarkInMgdl = 70.0
|
||||
static let defaultHighMmarkInMgdl = 170.0
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
enum ConstantsBloodGlucose {
|
||||
static let mmollToMgdl = 18.01801801801802
|
||||
static let mgDlToMmoll = 0.0555
|
||||
static let libreMultiplier = 117.64705
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
/// constants related to Bluetooth pairing
|
||||
enum ConstantsBluetoothPairing {
|
||||
|
||||
/// minimum time in seconds between two pairing notifications
|
||||
static let minimumTimeBetweenTwoPairingNotificationsInSeconds = 30
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/// constants used in calibration algorithm
|
||||
enum ConstantsCalibrationAlgorithms {
|
||||
// age adjustment constants, only for non Libre
|
||||
static let ageAdjustmentTime = 86400000 * 1.9
|
||||
static let ageAdjustmentFactor = 0.45
|
||||
|
||||
// minimum and maxium values for a reading
|
||||
static let minimumBgReadingCalculatedValue = 39.0
|
||||
static let maximumBgReadingCalculatedValue = 400.0
|
||||
static let bgReadingErrorValue = 38.0
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
/// coredata specific constants
|
||||
enum ConstantsCoreData {
|
||||
static let modelName = "xdrip"
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/// default alert levels to be used when creating defalt alert entries
|
||||
enum ConstantsDefaultAlertLevels {
|
||||
// default battery alert level, below this level an alert should be generated - this default value will be used when changing transmittertype
|
||||
static let defaultBatteryAlertLevelDexcomG5 = 300
|
||||
static let defaultBatteryAlertLevelDexcomG4 = 210
|
||||
static let defaultBatteryAlertLevelMiaoMiao = 20
|
||||
static let defaultBatteryAlertLevelGNSEntry = 20
|
||||
static let defaultBatteryAlertLevelBlucon = 20
|
||||
|
||||
// blood glucose level alert values in mgdl
|
||||
static let veryHigh = 250
|
||||
static let veryLow = 50
|
||||
static let high = 170
|
||||
static let low = 70
|
||||
|
||||
// in minutes, after how many minutes of now reading should alert be raised
|
||||
static let missedReading = 30
|
||||
|
||||
// in hours, after how many hours alert to request a new calibration
|
||||
static let calibration = 24
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/// default values to be used when creating a new AlertType
|
||||
enum ConstantsDefaultAlertTypeSettings {
|
||||
|
||||
static let enabled = true
|
||||
static let name = Texts_Common.default0
|
||||
static let overrideMute = false
|
||||
static let snooze = true
|
||||
static let snoozePeriod = Int16(60)
|
||||
static let vibrate = true
|
||||
static let soundName:String? = nil
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
/// constants for follower mode
|
||||
enum ConstantsFollower {
|
||||
|
||||
/// maximum days of readings to download
|
||||
static let maxiumDaysOfReadingsToDownload = 1
|
||||
|
||||
/// maximum age in seconds, of reading in alert flow. If age of latest reading is more than this number, then no alert check will be done
|
||||
static let maximumBgReadingAgeForAlertsInSeconds = 240.0
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
/// dexcom G5 specific constants
|
||||
enum ConstantsDexcomG5 {
|
||||
/// how often to read battery level
|
||||
static let batteryReadPeriodInHours = 12.0
|
||||
|
||||
/// in case transmitter needs pairing, how long to keep connection up to give time to the user to accept the pairing request, inclusive opening the notification
|
||||
static let maxTimeToAcceptPairingInSeconds = 60
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/// constants for Dexcom Share
|
||||
enum ConstantsDexcomShare {
|
||||
|
||||
/// applicationId to use in Dexcom Share protocol
|
||||
static let applicationId = "d8665ade-9673-4e27-9ff6-92db4ce13d13"
|
||||
|
||||
/// us share base url
|
||||
static let usBaseShareUrl = "https://share2.dexcom.com/ShareWebServices/Services"
|
||||
|
||||
/// non us share base url
|
||||
static let nonUsBaseShareUrl = "https://shareous1.dexcom.com/ShareWebServices/Services"
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/// constants for home view, ie first view
|
||||
enum ConstantsHomeView {
|
||||
|
||||
/// how often to update the labels in the homeview (ie label with latest reading, minutes ago, etc..)
|
||||
static let updateHomeViewIntervalInSeconds = 15.0
|
||||
|
||||
/// info email adres, appears in licenseInfo
|
||||
static let infoEmailAddress = "xdrip@proximus.be"
|
||||
|
||||
/// application name, appears in licenseInfo as title
|
||||
static let applicationName = "xDrip"
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/// for use in OSLog
|
||||
enum ConstantsLog {
|
||||
/// for use in OSLog
|
||||
static let subSystem = "xDrip"
|
||||
/// for use in OSLog
|
||||
static let categoryBlueTooth = "bluetooth"
|
||||
/// for use in cgm transmitter miaomiao
|
||||
static let categoryCGMMiaoMiao = "cgmmiaomiao"
|
||||
/// for use in cgm xdripg4
|
||||
static let categoryCGMxDripG4 = "cgmxdripg4"
|
||||
/// for use in firstview
|
||||
static let categoryFirstView = "firstview"
|
||||
/// calibration
|
||||
static let calibration = "Calibration"
|
||||
/// debuglogging
|
||||
static let debuglogging = "xdripdebuglogging"
|
||||
/// G5
|
||||
static let categoryCGMG5 = "categoryCGMG5"
|
||||
/// GNSEntry
|
||||
static let categoryCGMGNSEntry = "categoryCGMGNSEntry"
|
||||
/// Blucon
|
||||
static let categoryBlucon = "categoryBlucon"
|
||||
/// core data manager
|
||||
static let categoryCoreDataManager = "categoryCoreDataManager"
|
||||
/// application data bgreadings
|
||||
static let categoryApplicationDataBgReadings = "categoryApplicationDataBgReadings"
|
||||
/// application data calibrations
|
||||
static let categoryApplicationDataCalibrations = "categoryApplicationDataCalibrations"
|
||||
/// application data sensors
|
||||
static let categoryApplicationDataSensors = "categoryApplicationDataSensors"
|
||||
/// application data alerttypes
|
||||
static let categoryApplicationDataAlertTypes = "categoryApplicationDataAlertTypes"
|
||||
/// application data alertentries
|
||||
static let categoryApplicationDataAlertEntries = "categoryApplicationDataAlertEntries"
|
||||
/// nightscout uploader
|
||||
static let categoryNightScoutUploadManager = "categoryNightScoutUploadManager"
|
||||
/// nightscout follow
|
||||
static let categoryNightScoutFollowManager = "categoryNightScoutFollowManager"
|
||||
/// alertmanager
|
||||
static let categoryAlertManager = "categoryAlertManager"
|
||||
/// playsound
|
||||
static let categoryPlaySound = "categoryPlaySound"
|
||||
/// healthkit manager
|
||||
static let categoryHealthKitManager = "categoryHealthKitManager"
|
||||
/// SettingsViewHealthKitSettingsViewModel
|
||||
static let categorySettingsViewHealthKitSettingsViewModel = "categorySettingsViewHealthKitSettingsViewModel"
|
||||
/// dexcom share upload manager
|
||||
static let categoryDexcomShareUploadManager = "categoryDexcomShareUploadManager"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/// constants typically for master mode
|
||||
enum ConstantsMaster {
|
||||
|
||||
/// maximum age in seconds, of reading in alert flow. If age of latest reading is more than this number, then no alert check will be done
|
||||
static let maximumBgReadingAgeForAlertsInSeconds = 60.0
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
enum ConstantsNotifications {
|
||||
|
||||
/// identifiers for alert categories
|
||||
enum CategoryIdentifiersForAlerts {
|
||||
/// for initial calibration
|
||||
static let initialCalibrationRequest = "InititalCalibrationRequest"
|
||||
/// subsequent calibration request
|
||||
static let subsequentCalibrationRequest = "SubsequentCalibrationRequest"
|
||||
/// high alert
|
||||
static let highAlert = "highAlert"
|
||||
/// low alert
|
||||
static let lowAlert = "lowAlert"
|
||||
/// very high alert
|
||||
static let veryHighAlert = "veryHighAlert"
|
||||
/// very low alert
|
||||
static let veryLowAlert = "veryLowAlert"
|
||||
/// missed reading alert
|
||||
static let missedReadingAlert = "missedReadingAlert"
|
||||
/// battery low
|
||||
static let batteryLow = "batteryLow"
|
||||
}
|
||||
|
||||
/// identifiers for alert notifications
|
||||
enum NotificationIdentifiersForAlerts {
|
||||
/// high alert
|
||||
static let highAlert = "highAlert"
|
||||
/// low alert
|
||||
static let lowAlert = "lowAlert"
|
||||
/// very high alert
|
||||
static let veryHighAlert = "veryHighAlert"
|
||||
/// very low alert
|
||||
static let veryLowAlert = "veryLowAlert"
|
||||
/// missed reading alert
|
||||
static let missedReadingAlert = "missedReadingAlert"
|
||||
/// battery low
|
||||
static let batteryLow = "batteryLow"
|
||||
}
|
||||
|
||||
/// identifiers for calibration requests
|
||||
enum NotificationIdentifiersForCalibration {
|
||||
/// for initial calibration
|
||||
static let initialCalibrationRequest = "initialCalibrationRequest"
|
||||
/// subsequent calibration request
|
||||
static let subsequentCalibrationRequest = "subsequentCalibrationRequest"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForBgReading {
|
||||
/// bgreading notification
|
||||
static let bgReadingNotificationRequest = "bgReadingNotificationRequest"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForSensorNotDetected {
|
||||
/// sensor not detected notification
|
||||
static let sensorNotDetected = "sensorNotDetected"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForTransmitterNeedsPairing {
|
||||
/// transmitter needs pairing
|
||||
static let transmitterNeedsPairing = "transmitterNeedsPairing"
|
||||
}
|
||||
|
||||
enum NotificationIdentifierForResetResult {
|
||||
/// transmitter reset result
|
||||
static let transmitterResetResult = "transmitterResetResult"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/// defines name of the Soundfile and name of the sound shown to the user with an extra function - both are defined in one case, seperated by a backslash - to be used for alerts - all these sounds will be shown
|
||||
enum ConstantsSounds: String, CaseIterable {
|
||||
|
||||
// here using case iso properties because we want to iterate through them
|
||||
/// name of the sound as shown to the user, and also stored in the alerttype
|
||||
case batterwakeup = "Better Wake Up/betterwakeup.mp3"
|
||||
case bruteforce = "Brute Force/bruteforce.mp3"
|
||||
case modernalarm2 = "Modern Alert 2/modern2.mp3"
|
||||
case modernalarm = "Modern Alert/modernalarm.mp3"
|
||||
case shorthigh1 = "Short High 1/shorthigh1.mp3"
|
||||
case shorthigh2 = "Short High 2/shorthigh2.mp3"
|
||||
case shorthigh3 = "Short High 3/shorthigh3.mp3"
|
||||
case shorthigh4 = "Short High 4/shorthigh4.mp3"
|
||||
case shortlow1 = "Short Low 1/shortlow1.mp3"
|
||||
case shortlow2 = "Short Low 2/shortlow2.mp3"
|
||||
case shortlow3 = "Short Low 3/shortlow3.mp3"
|
||||
case shortlow4 = "Short Low 4/shortlow4.mp3"
|
||||
case spaceship = "Space Ship/spaceship.mp3"
|
||||
case xdripalert = "xDrip Alert/xdripalert.aif"
|
||||
|
||||
/// gets all sound names in array, ie part of the case before the /
|
||||
static func allSoundsBySoundNameAndFileName() -> (soundNames:[String], fileNames:[String]) {
|
||||
var soundNames = [String]()
|
||||
var soundFileNames = [String]()
|
||||
|
||||
soundloop: for sound in ConstantsSounds.allCases {
|
||||
|
||||
// ConstantsSounds defines available sounds. Per case there a string which is the soundname as shown in the UI and the filename of the sound in the Resources folder, seperated by backslash
|
||||
// get array of indexes, of location of "/"
|
||||
let indexOfBackSlash = sound.rawValue.indexes(of: "/")
|
||||
|
||||
// define range to get the soundname (as shown in UI)
|
||||
let soundNameRange = sound.rawValue.startIndex..<indexOfBackSlash[0]
|
||||
|
||||
// now get the soundName in a string
|
||||
let soundName = String(sound.rawValue[soundNameRange])
|
||||
|
||||
// add the soundName to the returnvalue
|
||||
soundNames.append(soundName)
|
||||
|
||||
// define range to get the soundFileName
|
||||
let languageCodeRange = sound.rawValue.index(after: indexOfBackSlash[0])..<sound.rawValue.endIndex
|
||||
|
||||
// now get the language in a string
|
||||
let fileName = String(sound.rawValue[languageCodeRange])
|
||||
// add the languageCode to the returnvalue
|
||||
|
||||
soundFileNames.append(fileName)
|
||||
|
||||
}
|
||||
return (soundNames, soundFileNames)
|
||||
}
|
||||
|
||||
/// gets the soundname for specific case
|
||||
static func getSoundName(forSound:ConstantsSounds) -> String {
|
||||
let indexOfBackSlash = forSound.rawValue.indexes(of: "/")
|
||||
let soundNameRange = forSound.rawValue.startIndex..<indexOfBackSlash[0]
|
||||
return String(forSound.rawValue[soundNameRange])
|
||||
}
|
||||
|
||||
/// gets the soundFie for specific case
|
||||
static func getSoundFile(forSound:ConstantsSounds) -> String {
|
||||
let indexOfBackSlash = forSound.rawValue.indexes(of: "/")
|
||||
let soundNameRange = forSound.rawValue.index(after: indexOfBackSlash[0])..<forSound.rawValue.endIndex
|
||||
return String(forSound.rawValue[soundNameRange])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/// supported languages for speak readings - defines name and language code, example "Dutch" and "nl-NL", both are defined in one case, seperated by a backslash
|
||||
///
|
||||
/// alphabetically ordered
|
||||
enum ConstantsSpeakReadingLanguages: String, CaseIterable {
|
||||
|
||||
case chinese = "Chinese/zh"
|
||||
case dutch = "Dutch/nl"
|
||||
case english = "English/en"
|
||||
case french = "French/fr"
|
||||
case italian = "Italian/it"
|
||||
case polish = "Polish/pl-PL"
|
||||
case portugese_portugal = "Portuguese/pt"
|
||||
case portugese_brasil = "Portuguese (Brazil)/pt-BR"
|
||||
case russian = "Russian/ru"
|
||||
case slovenian = "Slovenian/sl"
|
||||
case spanish_mexico = "Spanish (Mexico)/es-MX"
|
||||
case spanish_spain = "Spanish (Spain)/es-ES"
|
||||
|
||||
/// gets all language names and language codes in two arrays
|
||||
/// - returns:
|
||||
/// ie part of the case before the / in the first array, part of the case after the / in the second array
|
||||
public static var allLanguageNamesAndCodes: (names:[String], codes:[String]) {
|
||||
var languageNames = [String]()
|
||||
var languageCodes = [String]()
|
||||
|
||||
languageloop: for speakReadingLanguage in ConstantsSpeakReadingLanguages.allCases {
|
||||
|
||||
// SpeakReadingLanguages defines available languages. Per case there is a string which is the language as shown in the UI and the language code, seperated by backslash
|
||||
// get array of indexes, of location of "/"
|
||||
let indexOfBackSlash = speakReadingLanguage.rawValue.indexes(of: "/")
|
||||
|
||||
// define range to get the language (as shown in UI)
|
||||
let languageNameRange = speakReadingLanguage.rawValue.startIndex..<indexOfBackSlash[0]
|
||||
|
||||
// now get the language in a string
|
||||
let language = String(speakReadingLanguage.rawValue[languageNameRange])
|
||||
|
||||
// add the soundName to the returnvalue
|
||||
languageNames.append(language)
|
||||
|
||||
// define range to get the languagecode
|
||||
let languageCodeRange = speakReadingLanguage.rawValue.index(after: indexOfBackSlash[0])..<speakReadingLanguage.rawValue.endIndex
|
||||
|
||||
// now get the language in a string
|
||||
let languageCode = String(speakReadingLanguage.rawValue[languageCodeRange])
|
||||
// add the languageCode to the returnvalue
|
||||
|
||||
languageCodes.append(languageCode)
|
||||
|
||||
}
|
||||
return (languageNames, languageCodes)
|
||||
}
|
||||
|
||||
/// gets the language name for specific case
|
||||
static func languageName(forLanguageCode:String?) -> String {
|
||||
|
||||
if let forLanguageCode = forLanguageCode {
|
||||
for (index, languageCode) in allLanguageNamesAndCodes.codes.enumerated() {
|
||||
if languageCode == forLanguageCode {
|
||||
return allLanguageNamesAndCodes.names[index]
|
||||
}
|
||||
}
|
||||
}
|
||||
return Texts_SpeakReading.defaultLanguageCode
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
/// suspension prevention
|
||||
enum ConstantsSuspensionPrevention {
|
||||
|
||||
/// name of the file that has the sound to play
|
||||
static let soundFileName = "1-millisecond-of-silence.mp3"//20ms-of-silence.caf"
|
||||
|
||||
/// how often to play the sound, in seconds
|
||||
static let interval = 5
|
||||
}
|
||||
|
||||
|
|
@ -7,7 +7,7 @@ class AlertEntriesAccessor {
|
|||
// MARK: - Properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryApplicationDataAlertEntries)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryApplicationDataAlertEntries)
|
||||
|
||||
/// CoreDataManager to use
|
||||
private let coreDataManager:CoreDataManager
|
||||
|
|
|
@ -7,7 +7,7 @@ class AlertTypesAccessor {
|
|||
// MARK: - Properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryApplicationDataAlertTypes)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryApplicationDataAlertTypes)
|
||||
|
||||
/// CoreDataManager to use
|
||||
private let coreDataManager:CoreDataManager
|
||||
|
@ -48,7 +48,7 @@ class AlertTypesAccessor {
|
|||
if let alertType = alertTypes.first {
|
||||
return alertType
|
||||
} else {
|
||||
let defaultAlertType = AlertType(enabled: Constants.DefaultAlertTypeSettings.enabled, name: Texts_Common.default0, overrideMute: Constants.DefaultAlertTypeSettings.overrideMute, snooze: Constants.DefaultAlertTypeSettings.snooze, snoozePeriod: Int(Constants.DefaultAlertTypeSettings.snoozePeriod), vibrate: Constants.DefaultAlertTypeSettings.vibrate, soundName: Constants.Sounds.getSoundName(forSound: .xdripalert), alertEntries: nil, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
let defaultAlertType = AlertType(enabled: ConstantsDefaultAlertTypeSettings.enabled, name: Texts_Common.default0, overrideMute: ConstantsDefaultAlertTypeSettings.overrideMute, snooze: ConstantsDefaultAlertTypeSettings.snooze, snoozePeriod: Int(ConstantsDefaultAlertTypeSettings.snoozePeriod), vibrate: ConstantsDefaultAlertTypeSettings.vibrate, soundName: ConstantsSounds.getSoundName(forSound: .xdripalert), alertEntries: nil, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
|
||||
coreDataManager.saveChanges()
|
||||
return defaultAlertType
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ class BgReadingsAccessor {
|
|||
// MARK: - Properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryApplicationDataBgReadings)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryApplicationDataBgReadings)
|
||||
|
||||
/// CoreDataManager to use
|
||||
private let coreDataManager:CoreDataManager
|
||||
|
|
|
@ -7,7 +7,7 @@ class CalibrationsAccessor {
|
|||
// MARK: - Properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryApplicationDataCalibrations)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryApplicationDataCalibrations)
|
||||
|
||||
/// CoreDataManager to use
|
||||
private let coreDataManager:CoreDataManager
|
||||
|
|
|
@ -7,7 +7,7 @@ class SensorsAccessor {
|
|||
// MARK: - Properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryApplicationDataSensors)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryApplicationDataSensors)
|
||||
|
||||
/// CoreDataManager to use
|
||||
private let coreDataManager:CoreDataManager
|
||||
|
|
|
@ -10,7 +10,7 @@ import CoreData
|
|||
/// - overrideMute : should alert make sound if the alert is muted
|
||||
/// - snooze : can the alert be snoozed from the home screen notification
|
||||
/// - vibrate : should the phone vibrate when the alert fires
|
||||
/// - soundname : the name of the sound as shown in the UI. Constants.Sounds defines available sounds. Per case there a string which is the soundname as shown in the UI and the filename of the sound in the Resources folder, seperated by backslash. Some special cases : an empty string means no sound needed. A nil value means default iOS sound. Any other value should be in the list defined in Constants.Sounds, otherwise the default xDrip sound will be used (see AlertManager.swift)
|
||||
/// - soundname : the name of the sound as shown in the UI. ConstantsSounds defines available sounds. Per case there a string which is the soundname as shown in the UI and the filename of the sound in the Resources folder, seperated by backslash. Some special cases : an empty string means no sound needed. A nil value means default iOS sound. Any other value should be in the list defined in ConstantsSounds, otherwise the default xDrip sound will be used (see AlertManager.swift)
|
||||
/// - alertEntries : the alertEntries in which this AlertType is used, optional
|
||||
public class AlertType: NSManagedObject {
|
||||
init(
|
||||
|
|
|
@ -164,7 +164,7 @@ public class BgReading: NSManagedObject {
|
|||
return "???"
|
||||
}
|
||||
|
||||
if timeStamp.timeIntervalSince(previousBgReading.timeStamp) > Double(Constants.BGGraphBuilder.maxSlopeInMinutes * 60) {
|
||||
if timeStamp.timeIntervalSince(previousBgReading.timeStamp) > Double(ConstantsBGGraphBuilder.maxSlopeInMinutes * 60) {
|
||||
// don't show delta if there are not enough values or the values are more than 20 mintes apart
|
||||
return "???";
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ public class BgReading: NSManagedObject {
|
|||
func calculateSlope(lastBgReading:BgReading) -> (Double, Bool) {
|
||||
if timeStamp == lastBgReading.timeStamp
|
||||
||
|
||||
timeStamp.toMillisecondsAsDouble() - lastBgReading.timeStamp.toMillisecondsAsDouble() > Double(Constants.BGGraphBuilder.maxSlopeInMinutes * 60 * 1000) {
|
||||
timeStamp.toMillisecondsAsDouble() - lastBgReading.timeStamp.toMillisecondsAsDouble() > Double(ConstantsBGGraphBuilder.maxSlopeInMinutes * 60 * 1000) {
|
||||
return (0,true)
|
||||
}
|
||||
return ((lastBgReading.calculatedValue - calculatedValue) / (lastBgReading.timeStamp.toMillisecondsAsDouble() - timeStamp.toMillisecondsAsDouble()), false)
|
||||
|
|
|
@ -16,6 +16,8 @@ extension CBManagerState {
|
|||
return "unknown"
|
||||
case .unsupported:
|
||||
return "unsupported"
|
||||
@unknown default:
|
||||
return "unknown state"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import Foundation
|
|||
extension Double {
|
||||
/// converts mgdl to mmol
|
||||
func mgdlToMmol() -> Double {
|
||||
return self * Constants.BloodGlucose.mgDlToMmoll
|
||||
return self * ConstantsBloodGlucose.mgDlToMmoll
|
||||
}
|
||||
|
||||
/// converts mgdl to mmol if parameter mgdl = false. If mgdl = true then just returns self
|
||||
|
@ -11,7 +11,7 @@ extension Double {
|
|||
if mgdl {
|
||||
return self
|
||||
} else {
|
||||
return self * Constants.BloodGlucose.mgDlToMmoll
|
||||
return self * ConstantsBloodGlucose.mgDlToMmoll
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ extension Double {
|
|||
|
||||
/// converts mmol to mgdl
|
||||
func mmolToMgdl() -> Double {
|
||||
return self * Constants.BloodGlucose.mmollToMgdl
|
||||
return self * ConstantsBloodGlucose.mmollToMgdl
|
||||
}
|
||||
|
||||
/// returns the value rounded to fractionDigits
|
||||
|
|
|
@ -100,9 +100,21 @@ extension UserDefaults {
|
|||
/// timestamp of last bgreading that was stored in healthkit
|
||||
case timeStampLatestHealthKitStoreBgReading = "timeStampLatestHealthKitStoreBgReading"
|
||||
|
||||
// Dexcom Share
|
||||
/// timestamp of latest reading uploaded to Dexcom Share
|
||||
case timeStampLatestDexcomShareUploadedBgReading = "timeStampLatestDexcomShareUploadedBgReading"
|
||||
|
||||
// Sensor
|
||||
/// sensor Serial Number, for now only applicable to Libre
|
||||
case sensorSerialNumber = "sensorSerialNumber"
|
||||
|
||||
// development settings to test G6 scaling
|
||||
/// G6 factor1 - for testing G6 scaling
|
||||
case G6v2ScalingFactor1 = "G6v2ScalingFactor1"
|
||||
|
||||
/// G6 factor2 - for testing G6 scaling
|
||||
case G6v2ScalingFactor2 = "G6v2ScalingFactor2"
|
||||
|
||||
}
|
||||
|
||||
// MARK: - ===== User Configurable Settings ======
|
||||
|
@ -127,7 +139,7 @@ extension UserDefaults {
|
|||
var returnValue = double(forKey: Key.lowMarkValue.rawValue)
|
||||
// if 0 set to defaultvalue
|
||||
if returnValue == 0.0 {
|
||||
returnValue = Constants.BGGraphBuilder.defaultLowMarkInMgdl
|
||||
returnValue = ConstantsBGGraphBuilder.defaultLowMarkInMgdl
|
||||
}
|
||||
if !bloodGlucoseUnitIsMgDl {
|
||||
returnValue = returnValue.mgdlToMmol()
|
||||
|
@ -147,7 +159,7 @@ extension UserDefaults {
|
|||
var returnValue = double(forKey: Key.highMarkValue.rawValue)
|
||||
// if 0 set to defaultvalue
|
||||
if returnValue == 0.0 {
|
||||
returnValue = Constants.BGGraphBuilder.defaultHighMmarkInMgdl
|
||||
returnValue = ConstantsBGGraphBuilder.defaultHighMmarkInMgdl
|
||||
}
|
||||
if !bloodGlucoseUnitIsMgDl {
|
||||
returnValue = returnValue.mgdlToMmol()
|
||||
|
@ -527,6 +539,38 @@ extension UserDefaults {
|
|||
}
|
||||
}
|
||||
|
||||
/// sensor serial number, for now only useful for Libre sensor
|
||||
var sensorSerialNumber:String? {
|
||||
get {
|
||||
return string(forKey: Key.sensorSerialNumber.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.sensorSerialNumber.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ===== technical settings for testing ======
|
||||
|
||||
/// G6 factor 1
|
||||
@objc dynamic var G6v2ScalingFactor1:String? {
|
||||
get {
|
||||
return string(forKey: Key.G6v2ScalingFactor1.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.G6v2ScalingFactor1.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
/// G6 factor 2
|
||||
@objc dynamic var G6v2ScalingFactor2:String? {
|
||||
get {
|
||||
return string(forKey: Key.G6v2ScalingFactor2.rawValue)
|
||||
}
|
||||
set {
|
||||
set(newValue, forKey: Key.G6v2ScalingFactor2.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -42,22 +42,22 @@ public enum AlertKind:Int, CaseIterable {
|
|||
switch self {
|
||||
|
||||
case .low:
|
||||
return Constants.DefaultAlertLevels.low
|
||||
return ConstantsDefaultAlertLevels.low
|
||||
case .high:
|
||||
return Constants.DefaultAlertLevels.high
|
||||
return ConstantsDefaultAlertLevels.high
|
||||
case .verylow:
|
||||
return Constants.DefaultAlertLevels.veryLow
|
||||
return ConstantsDefaultAlertLevels.veryLow
|
||||
case .veryhigh:
|
||||
return Constants.DefaultAlertLevels.veryHigh
|
||||
return ConstantsDefaultAlertLevels.veryHigh
|
||||
case .missedreading:
|
||||
return Constants.DefaultAlertLevels.missedReading
|
||||
return ConstantsDefaultAlertLevels.missedReading
|
||||
case .calibration:
|
||||
return Constants.DefaultAlertLevels.calibration
|
||||
return ConstantsDefaultAlertLevels.calibration
|
||||
case .batterylow:
|
||||
if let transmitterType = UserDefaults.standard.transmitterType {
|
||||
return transmitterType.defaultBatteryAlertLevel()
|
||||
} else {
|
||||
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelMiaoMiao
|
||||
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelMiaoMiao
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -253,19 +253,19 @@ public enum AlertKind:Int, CaseIterable {
|
|||
switch self {
|
||||
|
||||
case .low:
|
||||
return Constants.Notifications.NotificationIdentifiersForAlerts.lowAlert
|
||||
return ConstantsNotifications.NotificationIdentifiersForAlerts.lowAlert
|
||||
case .high:
|
||||
return Constants.Notifications.NotificationIdentifiersForAlerts.highAlert
|
||||
return ConstantsNotifications.NotificationIdentifiersForAlerts.highAlert
|
||||
case .verylow:
|
||||
return Constants.Notifications.NotificationIdentifiersForAlerts.veryLowAlert
|
||||
return ConstantsNotifications.NotificationIdentifiersForAlerts.veryLowAlert
|
||||
case .veryhigh:
|
||||
return Constants.Notifications.NotificationIdentifiersForAlerts.veryHighAlert
|
||||
return ConstantsNotifications.NotificationIdentifiersForAlerts.veryHighAlert
|
||||
case .missedreading:
|
||||
return Constants.Notifications.NotificationIdentifiersForAlerts.missedReadingAlert
|
||||
return ConstantsNotifications.NotificationIdentifiersForAlerts.missedReadingAlert
|
||||
case .calibration:
|
||||
return Constants.Notifications.NotificationIdentifiersForCalibration.subsequentCalibrationRequest
|
||||
return ConstantsNotifications.NotificationIdentifiersForCalibration.subsequentCalibrationRequest
|
||||
case .batterylow:
|
||||
return Constants.Notifications.NotificationIdentifiersForAlerts.batteryLow
|
||||
return ConstantsNotifications.NotificationIdentifiersForAlerts.batteryLow
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ public class AlertManager:NSObject {
|
|||
private let snoozeCategoryIdentifier = "snoozeCategoryIdentifier"
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryAlertManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryAlertManager)
|
||||
|
||||
/// BgReadings instance
|
||||
private let bgReadingsAccessor:BgReadingsAccessor
|
||||
|
@ -345,11 +345,11 @@ public class AlertManager:NSObject {
|
|||
soundToSet = ""
|
||||
} else {
|
||||
// a sound name has been found in the alertType different from empty string (ie a sound must be played and it's not the default iOS sound)
|
||||
// need to find the corresponding sound file name in Constants.Sounds
|
||||
// need to find the corresponding sound file name in ConstantsSounds
|
||||
// start by setting it to to xdripalert, because the soundname found in the alert type might not be found in the list of sounds stored in the resources (although that shouldn't happen)
|
||||
soundToSet = "xdripalert.aif"
|
||||
soundloop: for sound in Constants.Sounds.allCases {
|
||||
// Constants.Sounds defines available sounds. Per case there a string which is the soundname as shown in the UI and the filename of the sound in the Resources folder, seperated by backslash
|
||||
soundloop: for sound in ConstantsSounds.allCases {
|
||||
// ConstantsSounds defines available sounds. Per case there a string which is the soundname as shown in the UI and the filename of the sound in the Resources folder, seperated by backslash
|
||||
// get array of indexes, of location of "/"
|
||||
let indexOfBackSlash = sound.rawValue.indexes(of: "/")
|
||||
// define range to get the soundname (as shown in UI)
|
||||
|
|
|
@ -13,7 +13,7 @@ final class CoreDataManager {
|
|||
|
||||
private let modelName: String
|
||||
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryCoreDataManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryCoreDataManager)
|
||||
|
||||
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillTerminate
|
||||
private let applicationManagerKeySaveChanges = "coredatamanagersavechanges"
|
||||
|
|
|
@ -24,14 +24,14 @@ class DexcomShareUploadManager:NSObject {
|
|||
private let messageHandler:((String, String) -> Void)?
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryDexcomShareUploadManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryDexcomShareUploadManager)
|
||||
|
||||
/// dexcom share url to use, calculated property
|
||||
private var dexcomShareUrl:String {
|
||||
if UserDefaults.standard.useUSDexcomShareurl {
|
||||
return Constants.DexcomShare.usBaseShareUrl
|
||||
return ConstantsDexcomShare.usBaseShareUrl
|
||||
} else {
|
||||
return Constants.DexcomShare.nonUsBaseShareUrl
|
||||
return ConstantsDexcomShare.nonUsBaseShareUrl
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -521,7 +521,7 @@ class DexcomShareUploadManager:NSObject {
|
|||
let uploadData = try JSONSerialization.data(withJSONObject: [
|
||||
"accountName": dexcomShareAccountName,
|
||||
"password": dexcomSharePassword,
|
||||
"applicationId": Constants.DexcomShare.applicationId
|
||||
"applicationId": ConstantsDexcomShare.applicationId
|
||||
], options: [])
|
||||
|
||||
// get shared URLSession
|
||||
|
|
|
@ -11,7 +11,7 @@ public class HealthKitManager:NSObject {
|
|||
private let keyValueObserverTimeKeeper:KeyValueObserverTimeKeeper = KeyValueObserverTimeKeeper()
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryHealthKitManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryHealthKitManager)
|
||||
|
||||
/// reference to coredatamanager
|
||||
private var coreDataManager:CoreDataManager
|
||||
|
@ -86,6 +86,8 @@ public class HealthKitManager:NSObject {
|
|||
return false
|
||||
case .sharingAuthorized:
|
||||
break
|
||||
@unknown default:
|
||||
os_log("unknown authorizationstatus for healthkit - HealthKitManager.swift", log: self.log, type: .error)
|
||||
}
|
||||
|
||||
// all checks ok , return true
|
||||
|
|
|
@ -14,7 +14,7 @@ class NightScoutFollowManager:NSObject {
|
|||
private let keyValueObserverTimeKeeper:KeyValueObserverTimeKeeper = KeyValueObserverTimeKeeper()
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryNightScoutFollowManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryNightScoutFollowManager)
|
||||
|
||||
/// when to do next download
|
||||
private var nextFollowDownloadTimeStamp:Date
|
||||
|
@ -59,7 +59,7 @@ class NightScoutFollowManager:NSObject {
|
|||
// creat audioplayer
|
||||
do {
|
||||
// set up url to create audioplayer
|
||||
let soundFileName = Constants.SuspensionPrevention.soundFileName
|
||||
let soundFileName = ConstantsSuspensionPrevention.soundFileName
|
||||
if let url = Bundle.main.url(forResource: soundFileName, withExtension: "") {
|
||||
|
||||
try audioPlayer = AVAudioPlayer(contentsOf: url)
|
||||
|
@ -144,7 +144,7 @@ class NightScoutFollowManager:NSObject {
|
|||
guard let nightScoutUrl = UserDefaults.standard.nightScoutUrl else {return}
|
||||
|
||||
// maximum timeStamp to download initially set to 1 day back
|
||||
var timeStampOfFirstBgReadingToDowload = Date(timeIntervalSinceNow: TimeInterval(-Constants.Follower.maxiumDaysOfReadingsToDownload * 24 * 3600))
|
||||
var timeStampOfFirstBgReadingToDowload = Date(timeIntervalSinceNow: TimeInterval(-ConstantsFollower.maxiumDaysOfReadingsToDownload * 24 * 3600))
|
||||
|
||||
// check timestamp of lastest stored bgreading with calculated value, if more recent then use this as timeStampOfFirstBgReadingToDowload
|
||||
let latestBgReadings = bgReadingsAccessor.getLatestBgReadings(limit: nil, howOld: 1, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
|
||||
|
@ -325,7 +325,7 @@ class NightScoutFollowManager:NSObject {
|
|||
private func enableSuspensionPrevention() {
|
||||
|
||||
// create playSoundTimer
|
||||
playSoundTimer = RepeatingTimer(timeInterval: TimeInterval(Constants.SuspensionPrevention.interval), eventHandler: {
|
||||
playSoundTimer = RepeatingTimer(timeInterval: TimeInterval(ConstantsSuspensionPrevention.interval), eventHandler: {
|
||||
// play the sound
|
||||
if let audioPlayer = self.audioPlayer, !audioPlayer.isPlaying {
|
||||
audioPlayer.play()
|
||||
|
|
|
@ -19,7 +19,7 @@ public class NightScoutUploadManager:NSObject {
|
|||
private let nightScoutAuthTestPath = "/api/v1/experiments/test"
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryNightScoutUploadManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryNightScoutUploadManager)
|
||||
|
||||
/// BgReadingsAccessor instance
|
||||
private let bgReadingsAccessor:BgReadingsAccessor
|
||||
|
|
|
@ -36,3 +36,5 @@
|
|||
"settingsviews_speakreadingslanguageselection" = "Select Language";
|
||||
"settingsviews_speakBgReadingslanguage" = "Language";
|
||||
"settingsviews_resettransmitter" = "Reset Transmitter";
|
||||
"settingsviews_Version" = "Version";
|
||||
"settingsviews_license" = "License";
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.3.1</string>
|
||||
<string>2.4.2</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.3.1</string>
|
||||
<string>2.4.2</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
|
|
|
@ -88,4 +88,5 @@ class Texts_Common {
|
|||
static let no = {
|
||||
return NSLocalizedString("no", tableName: filename, bundle: Bundle.main, value: "no", comment: "literally no, without capital")
|
||||
}()
|
||||
|
||||
}
|
||||
|
|
|
@ -187,4 +187,14 @@ class Texts_SettingsView {
|
|||
return NSLocalizedString("settingsviews_speakRateMessage", tableName: filename, bundle: Bundle.main, value: "Value between 0 and 1", comment: "When clicking the rate setting, a pop up asks for the rate, this is the message displayed in the pop up")
|
||||
}()
|
||||
|
||||
// MARK: - Section Info
|
||||
|
||||
static let version = {
|
||||
return NSLocalizedString("settingsviews_Version", tableName: filename, bundle: Bundle.main, value: "Version", comment: "used in settings, section Info, title of the version setting")
|
||||
}()
|
||||
|
||||
static let license = {
|
||||
return NSLocalizedString("settingsviews_license", tableName: filename, bundle: Bundle.main, value: "License", comment: "used in settings, section Info, title of the license setting")
|
||||
}()
|
||||
|
||||
}
|
||||
|
|
|
@ -13,13 +13,13 @@ enum Texts_SpeakReading {
|
|||
|
||||
/// the language for speak reading texts, default en
|
||||
///
|
||||
/// Must be a valid language code, example "en-EN" or "en-US" but also "en" is allowed - should be a language code that exists in Constants.SpeakReadingLanguages - and the corresponding strings file must exist. Example there's only "en" for the moment, not en-GB or en-US
|
||||
/// Must be a valid language code, example "en-EN" or "en-US" but also "en" is allowed - should be a language code that exists in ConstantsSpeakReadingLanguages - and the corresponding strings file must exist. Example there's only "en" for the moment, not en-GB or en-US
|
||||
///
|
||||
/// if there's no folder languageCode.lproj (example fr.lproj if languageCode would be assigned to "fr") then the default language will be used ie en
|
||||
private(set) static var languageCode = defaultLanguageCode
|
||||
|
||||
/// name of currently selected language, should be matching value currently stored in user defaults - it can be used for performance reasons, to avoid that when needed the whole enum in Constants.SpeakReadingLanguages needs to be iterated through each time again
|
||||
private(set) static var languageName = Constants.SpeakReadingLanguages.languageName(forLanguageCode: languageCode)
|
||||
/// name of currently selected language, should be matching value currently stored in user defaults - it can be used for performance reasons, to avoid that when needed the whole enum in ConstantsSpeakReadingLanguages needs to be iterated through each time again
|
||||
private(set) static var languageName = ConstantsSpeakReadingLanguages.languageName(forLanguageCode: languageCode)
|
||||
|
||||
/// bundle to use, will be reassigned if user changes language for speak reading texts
|
||||
private static var bundle = Bundle(path: Bundle.main.path(forResource: defaultLanguageCode, ofType: "lproj")!)
|
||||
|
@ -33,7 +33,7 @@ enum Texts_SpeakReading {
|
|||
|
||||
/// set the language for speak reading texts, default en
|
||||
///
|
||||
/// Must be a valid language code, example "en-EN" or "en-US" but also "en" is allowed - should be a language code that exists in Constants.SpeakReadingLanguages - and the corresponding strings file must exist. Example there's only "en" for the moment
|
||||
/// Must be a valid language code, example "en-EN" or "en-US" but also "en" is allowed - should be a language code that exists in ConstantsSpeakReadingLanguages - and the corresponding strings file must exist. Example there's only "en" for the moment
|
||||
///
|
||||
/// if there's no folder languageCode.lproj (example fr.lproj if languageCode would be assigned to "fr") then the default language will be used ie en
|
||||
public static func setLanguageCode(code:String?) {
|
||||
|
@ -47,7 +47,7 @@ public static func setLanguageCode(code:String?) {
|
|||
bundle = Bundle(path: path)
|
||||
} else {
|
||||
// full languageCode doesn't work, try now to split by - and use the first part only
|
||||
// should never be in this branch if Constants.SpeakReadingLanguages is aligned with actual .lproj folders
|
||||
// should never be in this branch if ConstantsSpeakReadingLanguages is aligned with actual .lproj folders
|
||||
if languageCode.contains(find: "-") {
|
||||
let indexOfHyphen = languageCode.indexes(of: "-")
|
||||
let languageRange = languageCode.startIndex..<indexOfHyphen[0]
|
||||
|
@ -64,7 +64,7 @@ public static func setLanguageCode(code:String?) {
|
|||
}
|
||||
|
||||
// set languageName
|
||||
languageName = Constants.SpeakReadingLanguages.languageName(forLanguageCode: languageCode)
|
||||
languageName = ConstantsSpeakReadingLanguages.languageName(forLanguageCode: languageCode)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
|
||||
|
||||
/// for OS_log
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryCGMxDripG4)
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryCGMxDripG4)
|
||||
|
||||
/// transmitterId
|
||||
private let transmitterId:String
|
||||
|
@ -108,7 +108,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
}
|
||||
|
||||
// Data packet Acknowledgement, to put wixel to sleep
|
||||
_ = writeDataToPeripheral(data: Data(bytes: [0x02,0xF0]), type: .withoutResponse)
|
||||
_ = writeDataToPeripheral(data: Data([0x02,0xF0]), type: .withoutResponse)
|
||||
|
||||
if let glucoseData = result.glucoseData {
|
||||
var glucoseDataArray = [glucoseData]
|
||||
|
@ -116,7 +116,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
if let level = result.batteryLevel {
|
||||
transmitterBatteryInfo = TransmitterBatteryInfo.DexcomG4(level: level)
|
||||
}
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
case .beaconPacket?:
|
||||
os_log(" in peripheral didUpdateValueFor, received beaconPacket", log: log, type: .info)
|
||||
|
@ -149,7 +149,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
|
|||
if let batteryLevel = result.batteryLevel {
|
||||
transmitterBatteryInfo = TransmitterBatteryInfo.DexcomG4(level: batteryLevel)
|
||||
}
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,8 +6,10 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
// MARK: - properties
|
||||
|
||||
/// scaling factor, for DexcomG5 this will be 1, for DexcomG6 it will be 34
|
||||
var scalingFactor = 1.0
|
||||
/// G5 or G6 transmitter firmware version - only used internally, if nil then it was never received
|
||||
///
|
||||
/// created public because inheriting classes need it
|
||||
var firmwareVersion:String?
|
||||
|
||||
// MARK: UUID's
|
||||
|
||||
|
@ -78,14 +80,11 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
|
||||
|
||||
/// for OS_log
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryCGMG5)
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryCGMG5)
|
||||
|
||||
/// is G5 reset necessary or not
|
||||
private var G5ResetRequested:Bool
|
||||
|
||||
// G5 transmitter firmware version - only used internally, if nil then it was never received
|
||||
private var transmitterVersion:String?
|
||||
|
||||
// actual device address
|
||||
private var actualDeviceAddress:String?
|
||||
|
||||
|
@ -165,12 +164,26 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
let testdata = RawGlucoseData(timeStamp: Date(), glucoseLevelRaw: testAmount, glucoseLevelFiltered: testAmount)
|
||||
debuglogging("timestamp testdata = " + testdata.timeStamp.description + ", with amount = " + testAmount.description)
|
||||
var testdataasarray = [testdata]
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &testdataasarray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
testAmount = testAmount + 1
|
||||
}
|
||||
|
||||
// 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:
|
||||
/// - firmwareVersion : for G6, the scaling factor is firmware dependent. Parameter created optional although it is known at the moment the function is used
|
||||
/// - the value to be scaled
|
||||
/// this function can be override in CGMG6Transmitter, which can then return the scalingFactor , firmware dependent
|
||||
func scaleRawValue(firmwareVersion: String?, rawValue: Double) -> Double {
|
||||
|
||||
// for G5, the scaling is independent of the firmwareVersion
|
||||
// and there's no scaling to do
|
||||
return rawValue
|
||||
|
||||
}
|
||||
|
||||
// MARK: CBCentralManager overriden functions
|
||||
|
||||
override func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
||||
|
@ -401,10 +414,10 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
if let sensorDataRxMessage = SensorDataRxMessage(data: value) {
|
||||
|
||||
if transmitterVersion != nil {
|
||||
if firmwareVersion != nil {
|
||||
|
||||
// transmitterversion was already recceived, let's see if we need to get the batterystatus
|
||||
if Date() > Date(timeInterval: Constants.DexcomG5.batteryReadPeriodInHours * 60 * 60, since: timeStampOfLastBatteryReading) {
|
||||
if Date() > Date(timeInterval: ConstantsDexcomG5.batteryReadPeriodInHours * 60 * 60, since: timeStampOfLastBatteryReading) {
|
||||
os_log(" last battery reading was long time ago, requesting now", log: log, type: .info)
|
||||
if let writeControlCharacteristic = writeControlCharacteristic {
|
||||
_ = writeDataToPeripheral(data: BatteryStatusTxMessage().data, characteristicToWriteTo: writeControlCharacteristic, type: .withResponse)
|
||||
|
@ -435,9 +448,12 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
os_log(" last reading was less than 1 minute ago, ignoring", log: log, type: .info)
|
||||
} else {
|
||||
timeStampOfLastG5Reading = Date()
|
||||
let glucoseData = RawGlucoseData(timeStamp: sensorDataRxMessage.timestamp, glucoseLevelRaw: sensorDataRxMessage.unfiltered * scalingFactor, glucoseLevelFiltered: sensorDataRxMessage.filtered * scalingFactor)
|
||||
|
||||
let glucoseData = RawGlucoseData(timeStamp: sensorDataRxMessage.timestamp, glucoseLevelRaw: scaleRawValue(firmwareVersion: firmwareVersion, rawValue: sensorDataRxMessage.unfiltered), glucoseLevelFiltered: scaleRawValue(firmwareVersion: firmwareVersion, rawValue: sensorDataRxMessage.unfiltered))
|
||||
|
||||
var glucoseDataArray = [glucoseData]
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -538,7 +554,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
private func processBatteryStatusRxMessage(value:Data) {
|
||||
if let batteryStatusRxMessage = BatteryStatusRxMessage(data: value) {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.DexcomG5(voltageA: batteryStatusRxMessage.voltageA, voltageB: batteryStatusRxMessage.voltageB, resist: batteryStatusRxMessage.resist, runtime: batteryStatusRxMessage.runtime, temperature: batteryStatusRxMessage.temperature), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
} else {
|
||||
os_log("batteryStatusRxMessage is nil", log: log, type: .error)
|
||||
}
|
||||
|
@ -546,9 +562,12 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
|
||||
private func processTransmitterVersionRxMessage(value:Data) {
|
||||
if let transmitterVersionRxMessage = TransmitterVersionRxMessage(data: value) {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: transmitterVersionRxMessage.firmwareVersion.hexEncodedString(), hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: transmitterVersionRxMessage.firmwareVersion.hexEncodedString(), hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
// assign transmitterVersion
|
||||
transmitterVersion = transmitterVersionRxMessage.firmwareVersion.hexEncodedString()
|
||||
firmwareVersion = transmitterVersionRxMessage.firmwareVersion.hexEncodedString()
|
||||
|
||||
} else {
|
||||
os_log("transmitterVersionRxMessage is nil", log: log, type: .error)
|
||||
}
|
||||
|
@ -596,7 +615,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
|
|||
if let receiveAuthenticationCharacteristic = receiveAuthenticationCharacteristic {
|
||||
|
||||
// to make sure the Dexcom doesn't disconnect the next 60 seconds, this gives the user sufficient time to accept the pairing request, which will come next
|
||||
_ = writeDataToPeripheral(data: KeepAliveTxMessage(time: UInt8(Constants.DexcomG5.maxTimeToAcceptPairingInSeconds)).data, characteristicToWriteTo: receiveAuthenticationCharacteristic, type: .withResponse)
|
||||
_ = writeDataToPeripheral(data: KeepAliveTxMessage(time: UInt8(ConstantsDexcomG5.maxTimeToAcceptPairingInSeconds)).data, characteristicToWriteTo: receiveAuthenticationCharacteristic, type: .withResponse)
|
||||
|
||||
} else {
|
||||
os_log(" in sendKeepAliveMessage, receiveAuthenticationCharacteristic is nil, can not send KeepAliveTxMessage", log: log, type: .error)
|
||||
|
|
|
@ -16,7 +16,7 @@ struct AuthRequestTxMessage: TransmitterTxMessage {
|
|||
init() {
|
||||
let uuid = UUID().uuid
|
||||
|
||||
singleUseToken = Data(bytes: [uuid.0, uuid.1, uuid.2, uuid.3,
|
||||
singleUseToken = Data([uuid.0, uuid.1, uuid.2, uuid.3,
|
||||
uuid.4, uuid.5, uuid.6, uuid.7])
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ struct BatteryStatusRxMessage: TransmitterRxMessage {
|
|||
let temperature:Int
|
||||
|
||||
init?(data: Data) {
|
||||
guard data.count >= 12 && data.isCRCValid else {
|
||||
guard data.count >= 10 && data.isCRCValid else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,12 @@ struct BatteryStatusRxMessage: TransmitterRxMessage {
|
|||
voltageA = Int(data.uint16(position: 2))
|
||||
voltageB = Int(data.uint16(position: 4))
|
||||
resist = Int(data.uint16(position: 6))
|
||||
runtime = Int(data.uint16(position: 8))
|
||||
temperature = Int(data.uint8(position: 10))
|
||||
if data.count == 10 {// see https://github.com/NightscoutFoundation/xDrip/commit/b1fb0835a765a89ccc1bb8b216b0d6b2d21d66bb#diff-564e59f90a64b2928799ea4e30d81920
|
||||
runtime = -1
|
||||
} else {
|
||||
runtime = Int(data[8])
|
||||
}
|
||||
temperature = Int(data.uint8(position: 9))
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ enum DexcomTransmitterOpCode: UInt8 {
|
|||
|
||||
extension Data {
|
||||
init(for opcode: DexcomTransmitterOpCode) {
|
||||
self.init(bytes: [opcode.rawValue])
|
||||
self.init([opcode.rawValue])
|
||||
}
|
||||
|
||||
func starts(with opcode: DexcomTransmitterOpCode) -> Bool {
|
||||
|
|
|
@ -2,6 +2,14 @@ import Foundation
|
|||
|
||||
class CGMG6Transmitter: CGMG5Transmitter {
|
||||
|
||||
/// scaling factor for G6 firmware version 1
|
||||
private let G6v1ScalingFactor = 34.0
|
||||
|
||||
/// scaling factor 1 for G6 firmware version 2
|
||||
static let G6v2DefaultScalingFactor1 = 1151500000.0
|
||||
|
||||
static let G6v2DefaultScalingFactor2 = 110000.0
|
||||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID, 6 characters
|
||||
|
@ -10,7 +18,38 @@ class CGMG6Transmitter: CGMG5Transmitter {
|
|||
// call super.init
|
||||
super.init(address: address, transmitterID: transmitterID, delegate: delegate)
|
||||
|
||||
scalingFactor = 34.0
|
||||
}
|
||||
|
||||
override func scaleRawValue(firmwareVersion: String?, rawValue: Double) -> Double {
|
||||
|
||||
if let firmwareVersion = firmwareVersion {
|
||||
if firmwareVersion.startsWith("1") {
|
||||
|
||||
// G6-v1
|
||||
return rawValue * G6v1ScalingFactor;
|
||||
|
||||
} else {
|
||||
|
||||
var scalingFactor1 = CGMG6Transmitter.G6v2DefaultScalingFactor1
|
||||
if let factor = UserDefaults.standard.G6v2ScalingFactor1, let factorAsDouble = factor.toDouble() {
|
||||
scalingFactor1 = factorAsDouble
|
||||
}
|
||||
|
||||
var scalingFactor2 = CGMG6Transmitter.G6v2DefaultScalingFactor2
|
||||
if let factor = UserDefaults.standard.G6v2ScalingFactor2, let factorAsDouble = factor.toDouble() {
|
||||
scalingFactor2 = factorAsDouble
|
||||
}
|
||||
|
||||
// G6-v2
|
||||
return (rawValue - scalingFactor1) / scalingFactor2
|
||||
}
|
||||
} else {
|
||||
|
||||
// assumed G6-v1, although firmwareVersion will normally not be nil
|
||||
return rawValue * G6v1ScalingFactor;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,7 +62,7 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
case GNSentry = "GNSentry"
|
||||
|
||||
/// Blucon
|
||||
case Blucon = "Blucon NOT READY !"
|
||||
case Blucon = "Blucon"
|
||||
|
||||
/// does the transmitter need a transmitter id ?
|
||||
///
|
||||
|
@ -90,6 +90,8 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
/// if true, then a class conforming to the protocol CGMTransmitterDelegate will call newSensorDetected if it detects a new sensor is placed. Means there's no need to let the user start and stop a sensor
|
||||
///
|
||||
/// example MiaoMiao can detect new sensor, implementation should return true, Dexcom transmitter's can't
|
||||
///
|
||||
/// if true, then transmitterType must also be able to give the sensor age, ie sensorTimeInMinutes
|
||||
func canDetectNewSensor() -> Bool {
|
||||
|
||||
switch self {
|
||||
|
@ -107,7 +109,7 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
return false
|
||||
|
||||
case .Blucon:
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,19 +153,19 @@ enum CGMTransmitterType:String, CaseIterable {
|
|||
switch self {
|
||||
|
||||
case .dexcomG4:
|
||||
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelDexcomG4
|
||||
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelDexcomG4
|
||||
|
||||
case .dexcomG5, .dexcomG6:
|
||||
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelDexcomG5
|
||||
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelDexcomG5
|
||||
|
||||
case .miaomiao:
|
||||
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelMiaoMiao
|
||||
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelMiaoMiao
|
||||
|
||||
case .GNSentry:
|
||||
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelGNSEntry
|
||||
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelGNSEntry
|
||||
|
||||
case .Blucon:
|
||||
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelBlucon
|
||||
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelBlucon
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,10 @@ protocol CGMTransmitterDelegate:AnyObject {
|
|||
/// - sensorTimeInMinutes : sensor age in minutes, only if transmitter can give that info, eg MiaoMiao, otherwise nil
|
||||
/// - firmware : only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - hardware : only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - serialNumber : only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - serialNumber : transmitter serial number, only if transmitter can give that info, eg G5, otherwise nil
|
||||
/// - bootloader : for the moment only used by GNSentry, otherwise nil
|
||||
func cgmTransmitterInfoReceived(glucoseData:inout [RawGlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorState:LibreSensorState?, sensorTimeInMinutes:Int?, firmware:String?, hardware:String?, serialNumber:String?, bootloader:String?)
|
||||
/// - sensorSerialNumber : serial number of the sensor, only applicable for Libre transmitters (MiaoMiao, Blucon, ...)
|
||||
func cgmTransmitterInfoReceived(glucoseData:inout [RawGlucoseData], transmitterBatteryInfo:TransmitterBatteryInfo?, sensorState:LibreSensorState?, sensorTimeInMinutes:Int?, firmware:String?, hardware:String?, hardwareSerialNumber:String?, bootloader:String?, sensorSerialNumber:String?)
|
||||
|
||||
/// transmitter needs bluetooth pairing
|
||||
func cgmTransmitterNeedsPairing()
|
||||
|
|
|
@ -2,7 +2,9 @@ import Foundation
|
|||
|
||||
enum BluconTransmitterOpCode: String, CaseIterable {
|
||||
|
||||
case wakeUp = "cb010000"
|
||||
case wakeUpRequest = "cb010000"
|
||||
|
||||
case wakeUpResponse = "810a00"
|
||||
|
||||
case getPatchInfoRequest = "010d0900"
|
||||
|
||||
|
@ -12,6 +14,32 @@ enum BluconTransmitterOpCode: String, CaseIterable {
|
|||
|
||||
case sensorNotDetected = "8b1a02000f"
|
||||
|
||||
case sleep = "010c0e00"
|
||||
|
||||
case bluconAckResponse = "8b0a00"
|
||||
|
||||
case unknown1Command = "010d0b00"
|
||||
|
||||
case unknown1CommandResponse = "8bdb"
|
||||
|
||||
case unknown2Command = "010d0a00"
|
||||
|
||||
case unknown2CommandResponse = "8bda"
|
||||
|
||||
case getHistoricDataAllBlocksCommand = "010d0f02002b"
|
||||
|
||||
case multipleBlockResponseIndex = "8bdf"
|
||||
|
||||
case getNowDataIndex = "010d0e0103"
|
||||
|
||||
case singleBlockInfoResponsePrefix = "8bde"
|
||||
|
||||
case singleBlockInfoPrefix = "010d0e010"
|
||||
|
||||
case bluconBatteryLowIndication1 = "cb020000"
|
||||
|
||||
case bluconBatteryLowIndication2 = "cbdb0000"
|
||||
|
||||
/// iterates through all cases, and as soon as one is found that starts with valueReceived, then initializes with that case. If none found then returns nil
|
||||
public init?(withOpCodeValue: String) {
|
||||
for opCode in BluconTransmitterOpCode.allCases {
|
||||
|
@ -24,14 +52,16 @@ enum BluconTransmitterOpCode: String, CaseIterable {
|
|||
return nil
|
||||
}
|
||||
|
||||
|
||||
public var description:String {
|
||||
|
||||
switch self {
|
||||
|
||||
case .wakeUp:
|
||||
case .wakeUpRequest:
|
||||
return "wakeUp"
|
||||
|
||||
case .wakeUpResponse:
|
||||
return "wakeUpResponse"
|
||||
|
||||
case .getPatchInfoRequest:
|
||||
return "getPatchInfo"
|
||||
|
||||
|
@ -43,6 +73,46 @@ enum BluconTransmitterOpCode: String, CaseIterable {
|
|||
|
||||
case .getPatchInfoResponse:
|
||||
return "getPatchInfoResponse"
|
||||
|
||||
case .sleep:
|
||||
return "sleep"
|
||||
|
||||
case .bluconAckResponse:
|
||||
return "bluconAckResponse"
|
||||
|
||||
case .unknown1Command:
|
||||
return "unknown1Command"
|
||||
|
||||
case .unknown1CommandResponse:
|
||||
return "unknown1CommandResponse"
|
||||
|
||||
case .unknown2Command:
|
||||
return "unknown2Command"
|
||||
|
||||
case .unknown2CommandResponse:
|
||||
return "unknown2CommandResponse"
|
||||
|
||||
case .getHistoricDataAllBlocksCommand:
|
||||
return "getHistoricDataAllBlocksCommand"
|
||||
|
||||
case .multipleBlockResponseIndex:
|
||||
return "multipleBlockResponseIndex"
|
||||
|
||||
case .getNowDataIndex:
|
||||
return "getNowDataIndex"
|
||||
|
||||
case .singleBlockInfoResponsePrefix:
|
||||
return "singleBlockInfoResponsePrefixResponse"
|
||||
|
||||
case .singleBlockInfoPrefix:
|
||||
return "singleBlockInfoPrefix"
|
||||
|
||||
case .bluconBatteryLowIndication1:
|
||||
return "bluconBatteryLowIndication1"
|
||||
|
||||
case .bluconBatteryLowIndication2:
|
||||
return "bluconBatteryLowIndication2"
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,39 +1,46 @@
|
|||
import Foundation
|
||||
|
||||
fileprivate let lookupTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "T", "U", "V", "W", "X", "Y", "Z"]
|
||||
|
||||
/// static functions for Blucon
|
||||
class BluconUtilities {
|
||||
|
||||
/*public static func decodeSerialNumber(input: Data) -> String{
|
||||
|
||||
let uuidShort = Data(bytes: <#T##Sequence#>)//new byte[]{0, 0, 0, 0, 0, 0, 0, 0};
|
||||
int i;
|
||||
|
||||
for (i = 2; i < 8; i++) uuidShort[i - 2] = input[(2 + 8) - i];
|
||||
uuidShort[6] = 0x00;
|
||||
uuidShort[7] = 0x00;
|
||||
|
||||
String binary = "";
|
||||
String binS = "";
|
||||
for (i = 0; i < 8; i++) {
|
||||
binS = String.format("%8s", Integer.toBinaryString(uuidShort[i] & 0xFF)).replace(' ', '0');
|
||||
binary += binS;
|
||||
}
|
||||
|
||||
String v = "0";
|
||||
char[] pozS = {0, 0, 0, 0, 0};
|
||||
for (i = 0; i < 10; i++) {
|
||||
for (int k = 0; k < 5; k++) pozS[k] = binary.charAt((5 * i) + k);
|
||||
int value = (pozS[0] - '0') * 16 + (pozS[1] - '0') * 8 + (pozS[2] - '0') * 4 + (pozS[3] - '0') * 2 + (pozS[4] - '0') * 1;
|
||||
v += lookupTable[value];
|
||||
}
|
||||
Log.e(TAG, "decodeSerialNumber=" + v);
|
||||
|
||||
return v;
|
||||
}*/
|
||||
/// - parameters:
|
||||
/// - input : data received from Blucon
|
||||
/// - returns: The sensor serial number
|
||||
///
|
||||
/// decodes serial number, copied forp xdripplus , commit 2b25bfdf6a563aea16de63053aec5e0e3be16e5f
|
||||
public static func decodeSerialNumber(input: Data) -> String {
|
||||
|
||||
var uuidShort = Data([0, 0, 0, 0, 0, 0, 0, 0])
|
||||
|
||||
for i in 2..<8 {
|
||||
uuidShort[i - 2] = input[(2 + 8) - i]
|
||||
}
|
||||
|
||||
uuidShort[6] = 0x00
|
||||
uuidShort[7] = 0x00
|
||||
|
||||
var binary = ""
|
||||
for i in 0..<8 {
|
||||
var binS = String(uuidShort[i] & 0xFF, radix: 2)
|
||||
while binS.count < 8 {
|
||||
binS = "0" + binS
|
||||
}
|
||||
binary = binary + binS
|
||||
}
|
||||
var v = "0"
|
||||
var pozS = [0, 0, 0, 0, 0]
|
||||
for i in 0..<10 {
|
||||
for k in 0..<5 {
|
||||
let index = (5 * i) + k
|
||||
pozS[k] = binary[index..<(index + 1)] == "0" ? 0:1
|
||||
}
|
||||
|
||||
let value = pozS[0] * 16 + pozS[1] * 8 + pozS[2] * 4 + pozS[3] * 2 + pozS[4] * 1
|
||||
v += lookupTable[value]
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate let lookupTable = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"A", "C", "D", "E", "F", "G", "H", "J", "K", "L",
|
||||
"M", "N", "P", "Q", "R", "T", "U", "V", "W", "X",
|
||||
"Y", "Z"]
|
||||
|
|
|
@ -10,16 +10,19 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
|
||||
|
||||
/// Blucon Service
|
||||
let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
|
||||
private let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
|
||||
|
||||
/// receive characteristic
|
||||
let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
|
||||
private let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
|
||||
|
||||
/// write characteristic
|
||||
let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
|
||||
private let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
|
||||
|
||||
/// if value starts with this string, then it's assume that a battery low indication is sent by the Blucon
|
||||
private let unknownCommand2BatteryLowIndicator = "8bda02"
|
||||
|
||||
/// for OS_log
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryBlucon)
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryBlucon)
|
||||
|
||||
// actual device address
|
||||
private var actualDeviceAddress:String?
|
||||
|
@ -30,12 +33,47 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
// waiting successful pairing yes or not
|
||||
private var waitingSuccessfulPairing:Bool = false
|
||||
|
||||
// current sensor serial number, if nil then it's not known yet
|
||||
private var sensorSerialNumber:String?
|
||||
|
||||
/// used as parameter in call to cgmTransmitterDelegate.cgmTransmitterInfoReceived, when there's no glucosedata to send
|
||||
private var emptyArray: [RawGlucoseData] = []
|
||||
|
||||
/// timestamp when wakeUpResponse was sent to Blucon
|
||||
private var timeStampLastWakeUpResponse:Date?
|
||||
|
||||
/// BluconACKResponse will come in two different situations
|
||||
/// - after we have sent an ackwakeup command
|
||||
/// - after we have a sleep command
|
||||
/// shouldSendUnknown1CommandAfterReceivingWakeUpResponse and timeStampLastWakeUpResponsetime work together to determine the case
|
||||
private var shouldSendUnknown1CommandAfterReceivingBluconAckResponse = true
|
||||
|
||||
/// used when processing Blucon data packet
|
||||
private var timestampFirstPacketReception:Date?
|
||||
|
||||
// how long to wait for next packet before considering the session as failed
|
||||
private let maxWaitForHistoricDataInSeconds = 5.0
|
||||
|
||||
// receive buffer for Blucon packets
|
||||
private var rxBuffer:Data
|
||||
|
||||
// used in Blucon protocol
|
||||
private var nowGlucoseOffset:UInt8 = 0
|
||||
|
||||
// used in Blucon protocol - corresponds to m_getNowGlucoseDataCommand
|
||||
private var waitingForGlucoseData = false
|
||||
|
||||
// timestamp of sending singleBlockInfoPrefix command, if time of receiving singleBlockInfoResponsePrefix is too late, then reading will be ignored
|
||||
private var timeStampOfSendingSingleBlockInfoPrefix:Date?
|
||||
|
||||
// MARK: - public functions
|
||||
|
||||
/// - parameters:
|
||||
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
|
||||
/// - transmitterID: expected transmitterID
|
||||
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
|
||||
/// - delegate : CGMTransmitterDelegate
|
||||
/// - sensorSerialNumber : is needed to allow detection of a new sensor.
|
||||
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
|
||||
|
||||
// assign addressname and name or expected devicename
|
||||
// start by using expected device name
|
||||
|
@ -46,8 +84,14 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
actualDeviceAddress = address
|
||||
}
|
||||
|
||||
//initialize timeStampLastBgReading
|
||||
// initialize timeStampLastBgReading
|
||||
self.timeStampLastBgReading = timeStampLastBgReading
|
||||
|
||||
// initialize sensorSerialNumber
|
||||
self.sensorSerialNumber = sensorSerialNumber
|
||||
|
||||
// initialize rxbuffer
|
||||
rxBuffer = Data()
|
||||
|
||||
// initialize
|
||||
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_BluconService)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Blucon, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Blucon, startScanningAfterInit: CGMTransmitterType.Blucon.startScanningAfterInit())
|
||||
|
@ -71,14 +115,124 @@ class CGMBluconTransmitter: BluetoothTransmitter {
|
|||
|
||||
if !returnValue.uppercased().startsWith("BLU") {
|
||||
while returnValue.count < 5 {
|
||||
returnValue = "0" + returnValue;
|
||||
returnValue = "0" + returnValue
|
||||
}
|
||||
returnValue = "BLU" + returnValue;
|
||||
returnValue = "BLU" + returnValue
|
||||
}
|
||||
|
||||
return returnValue
|
||||
}
|
||||
|
||||
/// writes command to blucon, withResponse, and also logs the command
|
||||
private func sendCommandToBlucon(opcode:BluconTransmitterOpCode) {
|
||||
|
||||
os_log(" send opcode %{public}@ to Blucon", log: log, type: .info, opcode.description)
|
||||
_ = writeDataToPeripheral(data: Data(hexadecimalString: opcode.rawValue)!, type: .withResponse)
|
||||
|
||||
}
|
||||
|
||||
/// reset rxBuffer, reset timestampFirstPacketReception, stop packetRxMonitorTimer
|
||||
private func resetRxBuffer() {
|
||||
rxBuffer = Data()
|
||||
timestampFirstPacketReception = Date()
|
||||
}
|
||||
|
||||
/// process new historic data block received from Blucon, one block is the contents when receiving multipleBlockResponseIndex, inclusive the opcode - this is used if we ask all Libre data from the transmitter, which includes sensorTime and sensorStatus
|
||||
/// - returns:
|
||||
/// - did receive all data yes or no, if yes, then blucon can go to sleep
|
||||
/// also calls delegate with result of new readings
|
||||
private func handleNewHistoricData(block: Data) -> Bool {
|
||||
|
||||
//check if buffer needs to be reset
|
||||
if let timestampFirstPacketReception = timestampFirstPacketReception {
|
||||
if (Date() > timestampFirstPacketReception.addingTimeInterval(maxWaitForHistoricDataInSeconds - 1)) {
|
||||
os_log("in handleNewHistoricData, more than %{public}d seconds since last update - or first update since app launch, resetting buffer", log: log, type: .info,maxWaitForHistoricDataInSeconds)
|
||||
resetRxBuffer()
|
||||
}
|
||||
}
|
||||
|
||||
//add new packet to buffer, ignoring the opcode (2 bytes), the number of the next block (1 byte), and the number of blocks in the data (1 byte)
|
||||
rxBuffer.append(block[4..<block.count])
|
||||
|
||||
// if rxBuffer has reached minimum lenght, then start processing
|
||||
if rxBuffer.count >= 344 {
|
||||
|
||||
os_log("in handleNewHistoricData, reached minimum length, processing data", log: log, type: .info)
|
||||
|
||||
// crc check
|
||||
guard Crc.LibreCrc(data: &rxBuffer, headerOffset: 0) else {
|
||||
|
||||
os_log(" crc check failed, no further processing", log: log, type: .error)
|
||||
|
||||
// transmitter can go to sleep
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
//get readings from buffer and send to delegate
|
||||
var result = parseLibreData(data: &rxBuffer, timeStampLastBgReadingStoredInDatabase: timeStampLastBgReading, headerOffset: 0)
|
||||
|
||||
//TODO: sort glucosedata before calling newReadingsReceived
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: nil, sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if result.glucoseData.count > 0 {
|
||||
timeStampLastBgReading = result.glucoseData[0].timeStamp
|
||||
}
|
||||
|
||||
//reset the buffer
|
||||
resetRxBuffer()
|
||||
|
||||
// transmitter can go to sleep
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
// transmitter should send more data
|
||||
return false
|
||||
}
|
||||
|
||||
private func blockNumberForNowGlucoseData(input:Data) -> String {
|
||||
|
||||
// caculate byte position in sensor body, decrement index to get the index where the last valid BG reading is stored
|
||||
var nowGlucoseIndex2 = input[5] * 6 + 4 - 6
|
||||
|
||||
// adjust round robin
|
||||
if nowGlucoseIndex2 < 4 {
|
||||
nowGlucoseIndex2 = nowGlucoseIndex2 + 96
|
||||
}
|
||||
|
||||
// calculate the absolute block number which correspond to trend index
|
||||
let nowGlucoseIndex3 = 3 + (nowGlucoseIndex2/8)
|
||||
|
||||
// calculate offset of the 2 bytes in the block
|
||||
nowGlucoseOffset = nowGlucoseIndex2 % 8
|
||||
|
||||
|
||||
let nowGlucoseDataAsHexString = nowGlucoseIndex3.description
|
||||
|
||||
return nowGlucoseDataAsHexString
|
||||
}
|
||||
|
||||
private func nowGetGlucoseValue(input:Data) -> Double {
|
||||
|
||||
// example 8BDE07DB010F04C868DB01
|
||||
// value 1 = 0F
|
||||
// value 2 = 04
|
||||
//rawGlucose = (input[3 + nowGlucoseOffset + 1] & 0x0F) * 256 + input[3 + nowGlucoseOffset] = 1039
|
||||
let value1 = input[3 + Int(nowGlucoseOffset)]
|
||||
let value2 = input[3 + Int(nowGlucoseOffset) + 1]
|
||||
|
||||
let rawGlucose = Double((UInt16(value2 & 0x0F)<<8) | UInt16(value1 & 0xFF))
|
||||
|
||||
// rescale for Libre
|
||||
let curGluc = rawGlucose * ConstantsBloodGlucose.libreMultiplier
|
||||
|
||||
return(curGluc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension CGMBluconTransmitter: CGMTransmitter {
|
||||
|
@ -164,29 +318,220 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
|
|||
// get Opcode
|
||||
if let opCode = BluconTransmitterOpCode(withOpCodeValue: valueAsString) {
|
||||
|
||||
os_log(" opcode = %{public}@", log: log, type: .info, opCode.description)
|
||||
os_log(" received opcode = %{public}@ from Blucon", log: log, type: .info, opCode.description)
|
||||
|
||||
switch opCode {
|
||||
|
||||
case .wakeUp:
|
||||
// send getPatchInfo command
|
||||
_ = writeDataToPeripheral(data: Data(hexadecimalString: BluconTransmitterOpCode.getPatchInfoRequest.rawValue)!, type: .withResponse)
|
||||
|
||||
case .getPatchInfoRequest:
|
||||
// shouldn't receive this ?
|
||||
return
|
||||
case .getPatchInfoRequest, .wakeUpResponse, .sleep, .unknown1Command, .unknown2Command, .getHistoricDataAllBlocksCommand, .getNowDataIndex, .singleBlockInfoPrefix:
|
||||
// these are commands that app sends to Blucon, shouldn't receive any of them
|
||||
break
|
||||
|
||||
case .wakeUpRequest:
|
||||
|
||||
// start by setting waitingForGlucoseData to false, it might still have value true due to protocol error
|
||||
waitingForGlucoseData = false
|
||||
|
||||
// send getPatchInfoRequest
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getPatchInfoRequest)
|
||||
|
||||
// by default set battery level to 100
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 100), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
|
||||
case .error14:
|
||||
|
||||
// Blucon didn't receive the next command it was waiting for, need to wait 5 minutes
|
||||
os_log(" Timeout received, need to wait 5 minutes or push button to restart!", log: log, type: .error)
|
||||
|
||||
|
||||
// and send Blucon to sleep
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
case .sensorNotDetected:
|
||||
// Blucon didn't detected sensor, call delegate
|
||||
|
||||
// Blucon didn't detect sensor, call delegate
|
||||
cgmTransmitterDelegate?.sensorNotDetected()
|
||||
|
||||
// and send Blucon to sleep
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
case .getPatchInfoResponse:
|
||||
|
||||
// get serial number
|
||||
let newSerialNumber = BluconUtilities.decodeSerialNumber(input: value)
|
||||
|
||||
// verify serial number and if changed inform delegate
|
||||
if newSerialNumber != sensorSerialNumber {
|
||||
|
||||
os_log(" new sensor detected : %{public}@", log: log, type: .info, newSerialNumber)
|
||||
|
||||
sensorSerialNumber = newSerialNumber
|
||||
|
||||
// inform delegate about new sensor detected
|
||||
cgmTransmitterDelegate?.newSensorDetected()
|
||||
|
||||
// also reset timestamp last reading, to be sure that if new sensor is started, we get historic data
|
||||
timeStampLastBgReading = Date(timeIntervalSince1970: 0)
|
||||
|
||||
}
|
||||
|
||||
// read sensorState
|
||||
let sensorState = LibreSensorState(stateByte: value[17])
|
||||
|
||||
// if sensor is ready then send Ack, otherwise send sleep
|
||||
if sensorState == LibreSensorState.ready {
|
||||
|
||||
timeStampLastWakeUpResponse = Date()
|
||||
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = true
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.wakeUpResponse)
|
||||
|
||||
} else {
|
||||
|
||||
os_log(" sensorState = %{public}@", log: log, type: .info, sensorState.description)
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.sleep)
|
||||
|
||||
}
|
||||
|
||||
// inform delegate about sensorSerialNumber and sensorState
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: sensorState, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: sensorSerialNumber)
|
||||
|
||||
return
|
||||
|
||||
case .bluconAckResponse:
|
||||
|
||||
// BluconACKResponse will come in two different situations
|
||||
// 1) after we have sent an ackwakeup command ==> need to send unknown1Command
|
||||
// 2) after we have sent a sleep command ==> no command to send
|
||||
// to verify in which case we are, timeStampLastWakeUpResponse is used
|
||||
// assuming bluconAckResponse will arrive less than 5 seconds after having send wakeUpResponse
|
||||
if let timeStampLastWakeUpResponse = timeStampLastWakeUpResponse, abs(timeStampLastWakeUpResponse.timeIntervalSinceNow) < 5 && shouldSendUnknown1CommandAfterReceivingBluconAckResponse {
|
||||
|
||||
// set to false to be sure
|
||||
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
|
||||
|
||||
// send unknown1Command
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown1Command)
|
||||
|
||||
} else {
|
||||
|
||||
os_log(" no further processing, Blucon is sleeping now and should send a new reading in 5 minutes", log: log, type: .info)
|
||||
|
||||
}
|
||||
|
||||
case .unknown1CommandResponse:
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown2Command)
|
||||
|
||||
case .unknown2CommandResponse:
|
||||
|
||||
// check if there's a battery low indication
|
||||
if valueAsString.startsWith(unknownCommand2BatteryLowIndicator) {
|
||||
|
||||
// this is considered as battery level 5%
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 5), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
}
|
||||
|
||||
// if timeStampLastBgReading > 5 minutes ago, then we'll get historic data, otherwise just get the latest reading
|
||||
if abs(timeStampLastBgReading.timeIntervalSinceNow) > 5 * 60 + 10 {
|
||||
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getHistoricDataAllBlocksCommand)
|
||||
|
||||
} else {
|
||||
|
||||
// not asking for sensorAge as in Spike and xdripplus, we know the sensorAge because we started with getHistoricDataAllBlocksCommand
|
||||
sendCommandToBlucon(opcode: BluconTransmitterOpCode.getNowDataIndex)
|
||||
|
||||
}
|
||||
|
||||
case .multipleBlockResponseIndex:
|
||||
|
||||
if handleNewHistoricData(block: value) {
|
||||
|
||||
// when Blucon responds with bluconAckResponse, then there's no need to send unknown1Command
|
||||
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
|
||||
|
||||
// send sleep command
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
}
|
||||
|
||||
case .singleBlockInfoResponsePrefix:
|
||||
|
||||
if !waitingForGlucoseData {
|
||||
|
||||
// get blockNumber and compose command
|
||||
let commandToSend = BluconTransmitterOpCode.singleBlockInfoPrefix.rawValue + blockNumberForNowGlucoseData(input: value)
|
||||
|
||||
// convert command to hexstring, might fail if blockNumberForNowGlucoseData returned an invalid value
|
||||
if let commandToSendAsData = Data(hexadecimalString: commandToSend) {
|
||||
|
||||
os_log(" send %{public}@ to Blucon", log: log, type: .info, commandToSend)
|
||||
_ = writeDataToPeripheral(data: commandToSendAsData, type: .withResponse)
|
||||
|
||||
waitingForGlucoseData = true
|
||||
|
||||
} else {
|
||||
|
||||
os_log(" failed to convert commandToSend to Data", log: log, type: .error)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// reset waitingForGlucoseData to false as we will not wait for glucosedata, after having processed this reading
|
||||
waitingForGlucoseData = false
|
||||
|
||||
// to be sure that waitingForGlucoseData is not having value true due to having broken protcol, verify when SingleBlockInfoPrefix was sent
|
||||
if let timeStampOfSendingSingleBlockInfoPrefix = timeStampOfSendingSingleBlockInfoPrefix {
|
||||
// should be a matter of milliseconds, so take 2 seconds
|
||||
if abs(timeStampOfSendingSingleBlockInfoPrefix.timeIntervalSinceNow) > 2 {
|
||||
|
||||
os_log(" time since sending SingleBlockInfoPrefix is more than 2 seconds, ignoring this reading", log: log, type: .error)
|
||||
|
||||
// send sleep command
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// checking now timestamp of last reading, if less than 30 seconds old, then reading will be ignored, seems a bit late to do that check, but after a few tests it seems to be the best to continue up to here to make sure the Blucon stays in a consistent state.
|
||||
if abs(timeStampLastBgReading.timeIntervalSinceNow) < 30 {
|
||||
os_log(" last reading less than 30 seconds old, ignoring this one", log: log, type: .info)
|
||||
} else {
|
||||
|
||||
os_log(" creating glucoseValue", log: log, type: .info)
|
||||
|
||||
// create glucose reading with timestamp now
|
||||
timeStampLastBgReading = Date()
|
||||
|
||||
// get glucoseValue from value
|
||||
let glucoseValue = nowGetGlucoseValue(input: value)
|
||||
|
||||
let glucoseData = RawGlucoseData(timeStamp: timeStampLastBgReading, glucoseLevelRaw: glucoseValue, glucoseLevelFiltered: glucoseValue)
|
||||
var glucoseDataArray = [glucoseData]
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
}
|
||||
|
||||
sendCommandToBlucon(opcode: .sleep)
|
||||
|
||||
}
|
||||
|
||||
case .bluconBatteryLowIndication1:
|
||||
|
||||
// this is considered as battery level 3%
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 3), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
case .bluconBatteryLowIndication2:
|
||||
|
||||
// this is considered as battery level 2%
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: 2), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
|
|
@ -67,7 +67,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
|
||||
|
||||
/// for OS_log
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryCGMGNSEntry)
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryCGMGNSEntry)
|
||||
|
||||
/// used in parsing packet
|
||||
private var timeStampLastBgReadingInMinutes:Double
|
||||
|
@ -154,22 +154,22 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
actualSerialNumber = String(data: value, encoding: String.Encoding.utf8)
|
||||
// TODO : is this the serial number of the sensor ? if yes we can use this to detect new sensor ? as with blucon ?
|
||||
if let actualSerialNumber = actualSerialNumber {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: actualSerialNumber, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: actualSerialNumber, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
case .CBUUID_Firmware:
|
||||
actualFirmWareVersion = String(data: value, encoding: String.Encoding.utf8)
|
||||
if let actualFirmWareVersion = actualFirmWareVersion {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: actualFirmWareVersion, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: actualFirmWareVersion, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
}
|
||||
case .CBUUID_Bootloader:
|
||||
actualBootLoader = String(data: value, encoding: String.Encoding.utf8)
|
||||
if let actualBootLoader = actualBootLoader {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: actualBootLoader)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: actualBootLoader, sensorSerialNumber: nil)
|
||||
}
|
||||
case .CBUUID_BatteryLevel:
|
||||
let dataAsString = value.hexEncodedString()
|
||||
if let batteryLevel = Int(dataAsString, radix: 16) {
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryLevel), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryLevel), sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
} else {
|
||||
os_log(" in peripheralDidUpdateValueFor, could not read batterylevel, received hex value = %{public}@", log: log, type: .error , dataAsString)
|
||||
}
|
||||
|
@ -227,7 +227,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
// sometimes 0 values are received, skip those
|
||||
if readingValueInMgDl > 0 {
|
||||
if readingTimeStampInMinutes * 60 * 1000 < timeStampLastAddedGlucoseDataInMinutes * 60 * 1000 - (5 * 60 * 1000 - 10000) {
|
||||
let glucoseData = RawGlucoseData(timeStamp: Date(timeIntervalSince1970: Double(readingTimeStampInMinutes) * 60.0), glucoseLevelRaw: Double(readingValueInMgDl) * Constants.BloodGlucose.libreMultiplier)
|
||||
let glucoseData = RawGlucoseData(timeStamp: Date(timeIntervalSince1970: Double(readingTimeStampInMinutes) * 60.0), glucoseLevelRaw: Double(readingValueInMgDl) * ConstantsBloodGlucose.libreMultiplier)
|
||||
readings.append(glucoseData)
|
||||
timeStampLastAddedGlucoseDataInMinutes = readingTimeStampInMinutes
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
i = i + 1
|
||||
}
|
||||
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorState: sensorStatus, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes), firmware: actualFirmWareVersion, hardware: nil, serialNumber: actualSerialNumber, bootloader: actualBootLoader)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &readings, transmitterBatteryInfo: nil, sensorState: sensorStatus, sensorTimeInMinutes: Int(sensorElapsedTimeInMinutes), firmware: actualFirmWareVersion, hardware: nil, hardwareSerialNumber: actualSerialNumber, bootloader: actualBootLoader, sensorSerialNumber: nil)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if readings.count > 0 {
|
||||
|
|
|
@ -22,7 +22,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
let maxPacketResendRequests = 3;
|
||||
|
||||
/// for OS_log
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryCGMMiaoMiao)
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryCGMMiaoMiao)
|
||||
|
||||
// used in parsing packet
|
||||
private var timeStampLastBgReading:Date
|
||||
|
@ -31,7 +31,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
private var resendPacketCounter:Int = 0
|
||||
|
||||
/// used when processing MiaoMiao data packet
|
||||
private var startDate:Date
|
||||
private var timestampFirstPacketReception:Date
|
||||
// receive buffer for miaomiao packets
|
||||
private var rxBuffer:Data
|
||||
// how long to wait for next packet before sending startreadingcommand
|
||||
|
@ -56,7 +56,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
// initialize rxbuffer
|
||||
rxBuffer = Data()
|
||||
startDate = Date()
|
||||
timestampFirstPacketReception = Date()
|
||||
|
||||
//initialize timeStampLastBgReading
|
||||
self.timeStampLastBgReading = timeStampLastBgReading
|
||||
|
@ -70,7 +70,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
// MARK: - public functions
|
||||
|
||||
func sendStartReadingCommmand() -> Bool {
|
||||
if writeDataToPeripheral(data: Data.init(bytes: [0xF0]), type: .withoutResponse) {
|
||||
if writeDataToPeripheral(data: Data.init([0xF0]), type: .withoutResponse) {
|
||||
return true
|
||||
} else {
|
||||
os_log("in sendStartReadingCommmand, write failed", log: log, type: .error)
|
||||
|
@ -106,7 +106,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
if let value = characteristic.value {
|
||||
|
||||
//check if buffer needs to be reset
|
||||
if (Date() > startDate.addingTimeInterval(CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds - 1)) {
|
||||
if (Date() > timestampFirstPacketReception.addingTimeInterval(CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds - 1)) {
|
||||
os_log("in peripheral didUpdateValueFor, more than %{public}d seconds since last update - or first update since app launch, resetting buffer", log: log, type: .info, CGMMiaoMiaoTransmitter.maxWaitForpacketInSeconds)
|
||||
resetRxBuffer()
|
||||
}
|
||||
|
@ -129,11 +129,11 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
let firmware = String(describing: rxBuffer[14...15].hexEncodedString())
|
||||
let hardware = String(describing: rxBuffer[16...17].hexEncodedString())
|
||||
let batteryPercentage = Int(rxBuffer[13])
|
||||
|
||||
|
||||
//get readings from buffer and send to delegate
|
||||
var result = parseLibreData(data: &rxBuffer, timeStampLastBgReadingStoredInDatabase: timeStampLastBgReading, headerOffset: miaoMiaoHeaderLength)
|
||||
//TODO: sort glucosedata before calling newReadingsReceived
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, serialNumber: nil, bootloader: nil)
|
||||
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &result.glucoseData, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: result.sensorState, sensorTimeInMinutes: result.sensorTimeInMinutes, firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
|
||||
|
||||
//set timeStampLastBgReading to timestamp of latest reading in the response so that next time we parse only the more recent readings
|
||||
if result.glucoseData.count > 0 {
|
||||
|
@ -166,7 +166,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
// 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)) {
|
||||
if self.writeDataToPeripheral(data: Data.init(bytes: [0xD3, 0x01]), type: .withoutResponse) {
|
||||
if self.writeDataToPeripheral(data: Data.init([0xD3, 0x01]), type: .withoutResponse) {
|
||||
os_log("in peripheralDidUpdateValueFor, successfully sent 0xD3 and 0x01, confirm sensor change to MiaoMiao", log: self.log, type: .info)
|
||||
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(500)) {
|
||||
if !self.sendStartReadingCommmand() {
|
||||
|
@ -212,10 +212,10 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
|
|||
|
||||
// MARK: - helpers
|
||||
|
||||
/// reset rxBuffer, reset startDate, stop packetRxMonitorTimer, set resendPacketCounter to 0
|
||||
/// reset rxBuffer, reset startDate, set resendPacketCounter to 0
|
||||
private func resetRxBuffer() {
|
||||
rxBuffer = Data()
|
||||
startDate = Date()
|
||||
timestampFirstPacketReception = Date()
|
||||
resendPacketCounter = 0
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ func parseLibreData(data:inout Data, timeStampLastBgReadingStoredInDatabase:Date
|
|||
byte.append(data[headerOffset + (i * 6 + 28)])
|
||||
let glucoseLevelRaw = Double(getGlucoseRaw(bytes: byte))
|
||||
if (glucoseLevelRaw > 0) {
|
||||
glucoseData = RawGlucoseData(timeStamp: Date(timeIntervalSince1970: sensorStartTimeInMilliseconds/1000 + timeInMinutes * 60), glucoseLevelRaw: Double(getGlucoseRaw(bytes: byte)) * Constants.BloodGlucose.libreMultiplier)
|
||||
glucoseData = RawGlucoseData(timeStamp: Date(timeIntervalSince1970: sensorStartTimeInMilliseconds/1000 + timeInMinutes * 60), glucoseLevelRaw: Double(getGlucoseRaw(bytes: byte)) * ConstantsBloodGlucose.libreMultiplier)
|
||||
returnValue.append(glucoseData)
|
||||
timeStampLastAddedGlucoseData = timeStampOfNewGlucoseData
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ func parseLibreData(data:inout Data, timeStampLastBgReadingStoredInDatabase:Date
|
|||
byte.append(data[headerOffset + (i * 6 + 124)])
|
||||
let glucoseLevelRaw = Double(getGlucoseRaw(bytes: byte))
|
||||
if (glucoseLevelRaw > 0) {
|
||||
glucoseData = RawGlucoseData(timeStamp: Date(timeIntervalSince1970: sensorStartTimeInMilliseconds/1000 + timeInMinutes * 60), glucoseLevelRaw: Double(getGlucoseRaw(bytes: byte)) * Constants.BloodGlucose.libreMultiplier)
|
||||
glucoseData = RawGlucoseData(timeStamp: Date(timeIntervalSince1970: sensorStartTimeInMilliseconds/1000 + timeInMinutes * 60), glucoseLevelRaw: Double(getGlucoseRaw(bytes: byte)) * ConstantsBloodGlucose.libreMultiplier)
|
||||
returnValue.append(glucoseData)
|
||||
timeStampLastAddedGlucoseData = timeStampOfNewGlucoseData
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ import Foundation
|
|||
/// - notYetStarted: 0x01 sensor not yet started
|
||||
/// - starting: 0x02 sensor is in the starting phase
|
||||
/// - ready: 0x03 sensor is ready, i.e. in normal operation mode
|
||||
/// - stateFour: 0x04 state with yet unknown meaning
|
||||
/// - expired: 0x05 sensor is expired
|
||||
/// - expired: 0x04 sensor is expired, status after 14 days, less than 14,5 days
|
||||
/// - shutdown: 0x05 sensor stops operation after 15d after start
|
||||
/// - failure: 0x06 sensor has an error
|
||||
/// - unknown: any other state
|
||||
enum LibreSensorState {
|
||||
|
|
|
@ -35,7 +35,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
|
|||
private let startScanningAfterInit:Bool
|
||||
|
||||
// for OS_log,
|
||||
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryBlueTooth)
|
||||
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryBlueTooth)
|
||||
|
||||
/// centralManager
|
||||
private var centralManager: CBCentralManager?
|
||||
|
|
|
@ -2,7 +2,7 @@ import Foundation
|
|||
import os
|
||||
|
||||
fileprivate var log:OSLog = {
|
||||
let log:OSLog = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.debuglogging)
|
||||
let log:OSLog = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.debuglogging)
|
||||
return log
|
||||
}()
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ class SoundPlayer {
|
|||
// MARK: - properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryPlaySound)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryPlaySound)
|
||||
|
||||
/// audioplayer
|
||||
private var audioPlayer:AVAudioPlayer?
|
||||
|
|
|
@ -50,6 +50,11 @@ enum SettingsSelectedRowAction {
|
|||
/// (it's not the right place to define this, not a clear split view/model)
|
||||
case performSegue(withIdentifier: String)
|
||||
|
||||
/// to show Info to user, eg licenseInfo, with a title and a message
|
||||
///
|
||||
/// typical a pop up with a title and the message
|
||||
case showInfoText(title: String, message: String)
|
||||
|
||||
}
|
||||
|
||||
/* explanation UITableViewCell.AccessoryType
|
||||
|
|
|
@ -52,13 +52,16 @@ final class RootViewController: UIViewController {
|
|||
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - initiate pairing
|
||||
private let applicationManagerKeyInitiatePairing = "RootViewController-InitiatePairing"
|
||||
|
||||
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - initial calibration
|
||||
private let applicationManagerKeyInitialCalibration = "RootViewController-InitialCalibration"
|
||||
|
||||
// MARK: - Properties - other private properties
|
||||
|
||||
/// a reference to the CGMTransmitter currently in use - nil means there's none, because user hasn't selected yet all required settings
|
||||
private var cgmTransmitter:CGMTransmitter?
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryFirstView)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryFirstView)
|
||||
|
||||
/// coreDataManager to be used throughout the project
|
||||
private var coreDataManager:CoreDataManager?
|
||||
|
@ -123,7 +126,7 @@ final class RootViewController: UIViewController {
|
|||
// Setup Core Data Manager - setting up coreDataManager happens asynchronously
|
||||
// completion handler is called when finished. This gives the app time to already continue setup which is independent of coredata, like setting up the transmitter, start scanning
|
||||
// In the exceptional case that the transmitter would give a new reading before the DataManager is set up, then this new reading will be ignored
|
||||
coreDataManager = CoreDataManager(modelName: Constants.CoreData.modelName, completion: {
|
||||
coreDataManager = CoreDataManager(modelName: ConstantsCoreData.modelName, completion: {
|
||||
|
||||
self.setupApplicationData()
|
||||
|
||||
|
@ -172,7 +175,7 @@ final class RootViewController: UIViewController {
|
|||
|
||||
// if licenseinfo not yet accepted, show license info with only ok button
|
||||
if !UserDefaults.standard.licenseInfoAccepted {
|
||||
UIAlertController(title: Constants.HomeView.applicationName, message: Texts_HomeView.licenseInfo + Constants.HomeView.infoEmailAddress, actionHandler: {
|
||||
UIAlertController(title: ConstantsHomeView.applicationName, message: Texts_HomeView.licenseInfo + ConstantsHomeView.infoEmailAddress, actionHandler: {
|
||||
|
||||
// set licenseInfoAccepted to true
|
||||
UserDefaults.standard.licenseInfoAccepted = true
|
||||
|
@ -251,7 +254,11 @@ final class RootViewController: UIViewController {
|
|||
})
|
||||
}
|
||||
|
||||
private func processNewCGMInfo(glucoseData: inout [RawGlucoseData], sensorState: LibreSensorState?, firmware: String?, hardware: String?, transmitterBatteryInfo: TransmitterBatteryInfo?, sensorTimeInMinutes: Int?) {
|
||||
/// process new glucose data received from transmitter.
|
||||
/// - parameters:
|
||||
/// - glucoseData : array with new readings
|
||||
/// - sensorTimeInMinutes : should be present only if it's the first reading(s) being processed for a specific sensor and is needed if it's a transmitterType that returns true to the function canDetectNewSensor
|
||||
private func processNewGlucoseData(glucoseData: inout [RawGlucoseData], sensorTimeInMinutes: Int?) {
|
||||
|
||||
// check that calibrations and coredata manager is not nil
|
||||
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager else {
|
||||
|
@ -337,7 +344,7 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
|
||||
if let alertManager = alertManager {
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: Constants.Master.maximumBgReadingAgeForAlertsInSeconds)
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsMaster.maximumBgReadingAgeForAlertsInSeconds)
|
||||
}
|
||||
|
||||
if let healthKitManager = healthKitManager {
|
||||
|
@ -354,10 +361,6 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
// check transmitterBatteryInfo and if available store in settings
|
||||
if let transmitterBatteryInfo = transmitterBatteryInfo {
|
||||
UserDefaults.standard.transmitterBatteryInfo = transmitterBatteryInfo
|
||||
}
|
||||
}
|
||||
|
||||
// MARK:- observe function
|
||||
|
@ -457,7 +460,7 @@ final class RootViewController: UIViewController {
|
|||
// check if timer already exists, if so invalidate it
|
||||
invalidateUpdateLabelsTimer()
|
||||
// now recreate, schedule and return
|
||||
return Timer.scheduledTimer(timeInterval: Constants.HomeView.updateHomeViewIntervalInSeconds, target: self, selector: #selector(self.updateLabels), userInfo: nil, repeats: true)
|
||||
return Timer.scheduledTimer(timeInterval: ConstantsHomeView.updateHomeViewIntervalInSeconds, target: self, selector: #selector(self.updateLabels), userInfo: nil, repeats: true)
|
||||
}
|
||||
|
||||
// call scheduleUpdateLabelsTimer function now - as the function setupUpdateLabelsTimer is called from viewdidload, it will be called immediately after app launch
|
||||
|
@ -540,7 +543,7 @@ final class RootViewController: UIViewController {
|
|||
|
||||
// check alerts
|
||||
if let alertManager = self.alertManager {
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: Constants.Master.maximumBgReadingAgeForAlertsInSeconds)
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsMaster.maximumBgReadingAgeForAlertsInSeconds)
|
||||
}
|
||||
|
||||
// update labels
|
||||
|
@ -598,7 +601,7 @@ final class RootViewController: UIViewController {
|
|||
|
||||
case .Blucon:
|
||||
if let currentTransmitterId = UserDefaults.standard.transmitterId {
|
||||
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
|
||||
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
|
||||
calibrator = Libre1Calibrator()
|
||||
}
|
||||
|
||||
|
@ -622,11 +625,11 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
// creates initial calibration request notification
|
||||
/// creates initial calibration request notification
|
||||
private func createInitialCalibrationRequest() {
|
||||
|
||||
// first remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest])
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifiersForCalibration.initialCalibrationRequest])
|
||||
|
||||
// Create Notification Content
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
|
@ -641,7 +644,7 @@ final class RootViewController: UIViewController {
|
|||
notificationContent.sound = UNNotificationSound.init(named: UNNotificationSoundName.init(""))
|
||||
|
||||
// Create Notification Request
|
||||
let notificationRequest = UNNotificationRequest(identifier: Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest, content: notificationContent, trigger: nil)
|
||||
let notificationRequest = UNNotificationRequest(identifier: ConstantsNotifications.NotificationIdentifiersForCalibration.initialCalibrationRequest, content: notificationContent, trigger: nil)
|
||||
|
||||
// Add Request to User Notification Center
|
||||
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
|
||||
|
@ -649,9 +652,25 @@ final class RootViewController: UIViewController {
|
|||
os_log("Unable to Add Notification Request : %{public}@", log: self.log, type: .error, error.localizedDescription)
|
||||
}
|
||||
}
|
||||
|
||||
// we will not just count on it that the user will click the notification to open the app (assuming the app is in the background, if the app is in the foreground, then we come in another flow)
|
||||
// whenever app comes from-back to foreground, requestCalibration needs to be called
|
||||
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyInitialCalibration, closure: {
|
||||
|
||||
// first of all reremove from application key manager
|
||||
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitialCalibration)
|
||||
|
||||
// remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifiersForCalibration.initialCalibrationRequest])
|
||||
|
||||
// request the calibration
|
||||
self.requestCalibration(userRequested: false)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// creates bgreading notification
|
||||
/// creates bgreading notification
|
||||
private func createBgReadingNotification() {
|
||||
|
||||
// bgReadingsAccessor should not be nil at all, but let's not create a fatal error for that, there's already enough checks for it
|
||||
|
@ -673,10 +692,10 @@ final class RootViewController: UIViewController {
|
|||
}
|
||||
|
||||
// remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForBgReading.bgReadingNotificationRequest])
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifierForBgReading.bgReadingNotificationRequest])
|
||||
|
||||
// also remove the sensor not detected notification, if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected])
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected])
|
||||
|
||||
// Create Notification Content
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
|
@ -695,7 +714,7 @@ final class RootViewController: UIViewController {
|
|||
notificationContent.body = " "
|
||||
|
||||
// Create Notification Request
|
||||
let notificationRequest = UNNotificationRequest(identifier: Constants.Notifications.NotificationIdentifierForBgReading.bgReadingNotificationRequest, content: notificationContent, trigger: nil)
|
||||
let notificationRequest = UNNotificationRequest(identifier: ConstantsNotifications.NotificationIdentifierForBgReading.bgReadingNotificationRequest, content: notificationContent, trigger: nil)
|
||||
|
||||
// Add Request to User Notification Center
|
||||
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
|
||||
|
@ -921,16 +940,21 @@ final class RootViewController: UIViewController {
|
|||
|
||||
}
|
||||
|
||||
// stop the active sensor
|
||||
// stops the active sensor and sets sensorSerialNumber in UserDefaults to nil
|
||||
private func stopSensor() {
|
||||
|
||||
if let activeSensor = activeSensor, let coreDataManager = coreDataManager {
|
||||
activeSensor.endDate = Date()
|
||||
coreDataManager.saveChanges()
|
||||
}
|
||||
activeSensor = nil
|
||||
|
||||
// save the changes
|
||||
coreDataManager?.saveChanges()
|
||||
|
||||
activeSensor = nil
|
||||
|
||||
// reset also serialNubmer to nil
|
||||
UserDefaults.standard.sensorSerialNumber = nil
|
||||
|
||||
}
|
||||
|
||||
// start a new sensor, ask user for starttime
|
||||
|
@ -989,7 +1013,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
notificationContent.body = Texts_HomeView.transmitterResetResult + " : " + (successful ? Texts_HomeView.success : Texts_HomeView.failed)
|
||||
|
||||
// Create Notification Request
|
||||
let notificationRequest = UNNotificationRequest(identifier: Constants.Notifications.NotificationIdentifierForResetResult.transmitterResetResult, content: notificationContent, trigger: nil)
|
||||
let notificationRequest = UNNotificationRequest(identifier: ConstantsNotifications.NotificationIdentifierForResetResult.transmitterResetResult, content: notificationContent, trigger: nil)
|
||||
|
||||
// Add Request to User Notification Center
|
||||
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
|
||||
|
@ -1011,7 +1035,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
func successfullyPaired() {
|
||||
|
||||
// remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
|
||||
|
||||
// invalidate transmitterPairingResponseTimer
|
||||
if let transmitterPairingResponseTimer = transmitterPairingResponseTimer {
|
||||
|
@ -1066,6 +1090,8 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
_ = cgmTransmitter.startScanning()
|
||||
}
|
||||
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1078,7 +1104,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
if let timeStampLastNotificationForPairing = timeStampLastNotificationForPairing {
|
||||
|
||||
// check timestamp of last notification, if too soon then return
|
||||
if Int(abs(timeStampLastNotificationForPairing.timeIntervalSinceNow)) < Constants.BluetoothPairing.minimumTimeBetweenTwoPairingNotificationsInSeconds {
|
||||
if Int(abs(timeStampLastNotificationForPairing.timeIntervalSinceNow)) < ConstantsBluetoothPairing.minimumTimeBetweenTwoPairingNotificationsInSeconds {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -1087,7 +1113,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
timeStampLastNotificationForPairing = Date()
|
||||
|
||||
// remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
|
||||
|
||||
// Create Notification Content
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
|
@ -1101,7 +1127,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
notificationContent.sound = UNNotificationSound.init(named: UNNotificationSoundName.init(""))
|
||||
|
||||
// Create Notification Request
|
||||
let notificationRequest = UNNotificationRequest(identifier: Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing, content: notificationContent, trigger: nil)
|
||||
let notificationRequest = UNNotificationRequest(identifier: ConstantsNotifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing, content: notificationContent, trigger: nil)
|
||||
|
||||
// Add Request to User Notification Center
|
||||
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
|
||||
|
@ -1117,18 +1143,17 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
// If the app is already in the foreground, then userNotificationCenter willPresent will be called, in this function the closure will be removed immediately, and the pairing request will be called. As a result, if the app is in the foreground, the user will not see (or hear) any notification, but the pairing will be initiated
|
||||
|
||||
// max timestamp when notification was fired - connection stays open for 1 minute, taking 1 second as d
|
||||
let maxTimeUserCanOpenApp = Date(timeIntervalSinceNow: TimeInterval(Constants.DexcomG5.maxTimeToAcceptPairingInSeconds - 1))
|
||||
let maxTimeUserCanOpenApp = Date(timeIntervalSinceNow: TimeInterval(ConstantsDexcomG5.maxTimeToAcceptPairingInSeconds - 1))
|
||||
|
||||
// we will not just count on it that the user will click the notification to open the app (assuming the app is in the background, if the app is in the foreground, then we come in another flow)
|
||||
// we will
|
||||
// whenever app comes from-back to freground, updateLabels needs to be called
|
||||
// whenever app comes from-back to foreground, updateLabels needs to be called
|
||||
ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground(key: applicationManagerKeyInitiatePairing, closure: {
|
||||
|
||||
// first of all reremove from application key manager
|
||||
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitiatePairing)
|
||||
|
||||
// first remove existing notification if any
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
|
||||
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [ConstantsNotifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
|
||||
|
||||
// if it was too long since notification was fired, then forget about it
|
||||
if Date() > maxTimeUserCanOpenApp {
|
||||
|
@ -1163,7 +1188,7 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
notificationContent.body = Texts_HomeView.sensorNotDetected
|
||||
|
||||
// Create Notification Request
|
||||
let notificationRequest = UNNotificationRequest(identifier: Constants.Notifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected, content: notificationContent, trigger: nil)
|
||||
let notificationRequest = UNNotificationRequest(identifier: ConstantsNotifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected, content: notificationContent, trigger: nil)
|
||||
|
||||
// Add Request to User Notification Center
|
||||
UNUserNotificationCenter.current().add(notificationRequest) { (error) in
|
||||
|
@ -1175,18 +1200,32 @@ extension RootViewController:CGMTransmitterDelegate {
|
|||
|
||||
/// - parameters:
|
||||
/// - readings: first entry is the most recent
|
||||
func cgmTransmitterInfoReceived(glucoseData: inout [RawGlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorState: LibreSensorState?, sensorTimeInMinutes: Int?, firmware: String?, hardware: String?, serialNumber: String?, bootloader: String?) {
|
||||
func cgmTransmitterInfoReceived(glucoseData: inout [RawGlucoseData], transmitterBatteryInfo: TransmitterBatteryInfo?, sensorState: LibreSensorState?, sensorTimeInMinutes: Int?, firmware: String?, hardware: String?, hardwareSerialNumber: String?, bootloader: String?, sensorSerialNumber:String?) {
|
||||
|
||||
os_log("sensorstate %{public}@", log: log, type: .debug, sensorState?.description ?? "no sensor state found")
|
||||
os_log("firmware %{public}@", log: log, type: .debug, firmware ?? "no firmware version found")
|
||||
os_log("bootloader %{public}@", log: log, type: .debug, bootloader ?? "no bootloader found")
|
||||
os_log("serialNumber %{public}@", log: log, type: .debug, serialNumber ?? "no serialNumber found")
|
||||
os_log("hardwareSerialNumber %{public}@", log: log, type: .debug, hardwareSerialNumber ?? "no serialNumber found")
|
||||
os_log("sensorSerialNumber %{public}@", log: log, type: .debug, sensorSerialNumber ?? "no sensorSerialNumber found")
|
||||
os_log("hardware %{public}@", log: log, type: .debug, hardware ?? "no hardware version found")
|
||||
os_log("transmitterBatteryInfo %{public}@", log: log, type: .debug, transmitterBatteryInfo?.description ?? 0)
|
||||
os_log("sensor time in minutes %{public}@", log: log, type: .debug, sensorTimeInMinutes?.description ?? "not received")
|
||||
os_log("glucoseData size = %{public}@", log: log, type: .debug, glucoseData.count.description)
|
||||
|
||||
// if received sensorSerialNumber not nil, and if value different from currently stored value, then store it
|
||||
if let sensorSerialNumber = sensorSerialNumber {
|
||||
if sensorSerialNumber != UserDefaults.standard.sensorSerialNumber {
|
||||
UserDefaults.standard.sensorSerialNumber = sensorSerialNumber
|
||||
}
|
||||
}
|
||||
|
||||
processNewCGMInfo(glucoseData: &glucoseData, sensorState: sensorState, firmware: firmware, hardware: hardware, transmitterBatteryInfo: transmitterBatteryInfo, sensorTimeInMinutes: sensorTimeInMinutes)
|
||||
// if received transmitterBatteryInfo not nil, then store it
|
||||
if let transmitterBatteryInfo = transmitterBatteryInfo {
|
||||
UserDefaults.standard.transmitterBatteryInfo = transmitterBatteryInfo
|
||||
}
|
||||
|
||||
// process new readings
|
||||
processNewGlucoseData(glucoseData: &glucoseData, sensorTimeInMinutes: sensorTimeInMinutes)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1212,24 +1251,27 @@ extension RootViewController:UNUserNotificationCenterDelegate {
|
|||
// called when notification created while app is in foreground
|
||||
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
||||
|
||||
if notification.request.identifier == Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest {
|
||||
if notification.request.identifier == ConstantsNotifications.NotificationIdentifiersForCalibration.initialCalibrationRequest {
|
||||
|
||||
// request calibration
|
||||
requestCalibration(userRequested: false)
|
||||
|
||||
/// remove applicationManagerKeyInitialCalibration from application key manager - there's no need to initiate the calibration via this closure
|
||||
ApplicationManager.shared.removeClosureToRunWhenAppWillEnterForeground(key: self.applicationManagerKeyInitialCalibration)
|
||||
|
||||
// call completionhandler to avoid that notification is shown to the user
|
||||
completionHandler([])
|
||||
|
||||
} else if notification.request.identifier == Constants.Notifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected {
|
||||
} else if notification.request.identifier == ConstantsNotifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected {
|
||||
|
||||
// call completionhandler to show the notification even though the app is in the foreground, without sound
|
||||
completionHandler([.alert])
|
||||
|
||||
} else if notification.request.identifier == Constants.Notifications.NotificationIdentifierForResetResult.transmitterResetResult {
|
||||
} else if notification.request.identifier == ConstantsNotifications.NotificationIdentifierForResetResult.transmitterResetResult {
|
||||
|
||||
completionHandler([.alert])
|
||||
|
||||
} else if notification.request.identifier == Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing {
|
||||
} else if notification.request.identifier == ConstantsNotifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing {
|
||||
|
||||
// so actually the app was in the foreground, at the moment the Transmitter Class called the cgmTransmitterNeedsPairing function, there's no need to show the notification, we can immediately call back the cgmTransmitter initiatePairing function
|
||||
completionHandler([])
|
||||
|
@ -1260,22 +1302,21 @@ extension RootViewController:UNUserNotificationCenterDelegate {
|
|||
completionHandler()
|
||||
}
|
||||
|
||||
if response.notification.request.identifier == Constants.Notifications.NotificationIdentifiersForCalibration.initialCalibrationRequest {
|
||||
if response.notification.request.identifier == ConstantsNotifications.NotificationIdentifiersForCalibration.initialCalibrationRequest {
|
||||
|
||||
os_log(" userNotificationCenter didReceive, user pressed calibration notification to open the app", log: log, type: .info)
|
||||
// request calibration
|
||||
requestCalibration(userRequested: false)
|
||||
// nothing required, the requestCalibration function will be called as it's been added to ApplicationManager
|
||||
os_log(" userNotificationCenter didReceive, user pressed calibration notification to open the app, requestCalibration should be called because closure is added in ApplicationManager.shared", log: log, type: .info)
|
||||
|
||||
} else if response.notification.request.identifier == Constants.Notifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected {
|
||||
} else if response.notification.request.identifier == ConstantsNotifications.NotificationIdentifierForSensorNotDetected.sensorNotDetected {
|
||||
|
||||
// if user clicks notification "sensor not detected", then show uialert with title and body
|
||||
UIAlertController(title: Texts_Common.warning, message: Texts_HomeView.sensorNotDetected, actionHandler: nil).presentInOwnWindow(animated: true, completion: nil)
|
||||
|
||||
} else if response.notification.request.identifier == Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing {
|
||||
} else if response.notification.request.identifier == ConstantsNotifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing {
|
||||
|
||||
// nothing required, the pairing function will be called as it's been added to ApplicationManager in function cgmTransmitterNeedsPairing
|
||||
|
||||
} else if response.notification.request.identifier == Constants.Notifications.NotificationIdentifierForResetResult.transmitterResetResult {
|
||||
} else if response.notification.request.identifier == ConstantsNotifications.NotificationIdentifierForResetResult.transmitterResetResult {
|
||||
|
||||
// nothing required
|
||||
|
||||
|
@ -1341,7 +1382,7 @@ extension RootViewController:NightScoutFollowerDelegate {
|
|||
|
||||
// check alerts
|
||||
if let alertManager = alertManager {
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: Constants.Follower.maximumBgReadingAgeForAlertsInSeconds)
|
||||
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsFollower.maximumBgReadingAgeForAlertsInSeconds)
|
||||
}
|
||||
|
||||
if let healthKitManager = healthKitManager {
|
||||
|
|
|
@ -65,13 +65,13 @@ final class AlertTypeSettingsViewController: UIViewController {
|
|||
// MARK:- alerttype temp properties
|
||||
|
||||
// following properties are used to temporary store alertType attributes which can be modified. The actual update of the alertType being processed will be done only when the user clicks the done button
|
||||
private var enabled = Constants.DefaultAlertTypeSettings.enabled
|
||||
private var name = Constants.DefaultAlertTypeSettings.name
|
||||
private var overrideMute = Constants.DefaultAlertTypeSettings.overrideMute
|
||||
private var snooze = Constants.DefaultAlertTypeSettings.snooze
|
||||
private var snoozePeriod = Constants.DefaultAlertTypeSettings.snoozePeriod
|
||||
private var vibrate = Constants.DefaultAlertTypeSettings.vibrate
|
||||
private var soundName = Constants.DefaultAlertTypeSettings.soundName
|
||||
private var enabled = ConstantsDefaultAlertTypeSettings.enabled
|
||||
private var name = ConstantsDefaultAlertTypeSettings.name
|
||||
private var overrideMute = ConstantsDefaultAlertTypeSettings.overrideMute
|
||||
private var snooze = ConstantsDefaultAlertTypeSettings.snooze
|
||||
private var snoozePeriod = ConstantsDefaultAlertTypeSettings.snoozePeriod
|
||||
private var vibrate = ConstantsDefaultAlertTypeSettings.vibrate
|
||||
private var soundName = ConstantsDefaultAlertTypeSettings.soundName
|
||||
|
||||
// MARK:- public functions
|
||||
|
||||
|
@ -299,7 +299,7 @@ extension AlertTypeSettingsViewController: UITableViewDataSource, UITableViewDel
|
|||
|
||||
case .soundName:
|
||||
// create array of all sounds and sound filenames, inclusive default ios sound and also empty string, which is "no sound"
|
||||
var sounds = Constants.Sounds.allSoundsBySoundNameAndFileName()
|
||||
var sounds = ConstantsSounds.allSoundsBySoundNameAndFileName()
|
||||
sounds.soundNames.insert(Texts_AlertTypeSettingsView.alertTypeDefaultIOSSound, at: 0)
|
||||
sounds.soundNames.insert(Texts_AlertTypeSettingsView.alertTypeNoSound, at: 0)
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ final class SettingsViewController: UIViewController {
|
|||
fileprivate var healthKitSettingsViewModel = SettingsViewHealthKitSettingsViewModel()
|
||||
fileprivate var alarmsSettingsViewModel = SettingsViewAlertSettingsViewModel()
|
||||
fileprivate var speakSettingsViewModel = SettingsViewSpeakSettingsViewModel()
|
||||
fileprivate var developmentSettingsViewModel = SettingsViewDevelopmentSettingsViewModel()
|
||||
fileprivate var infoSettingsViewModel = SettingsViewInfoViewModel()
|
||||
|
||||
private lazy var pickerViewController: PickerViewController = {
|
||||
// Instantiate View Controller
|
||||
|
@ -132,6 +134,10 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
case healthkit
|
||||
/// store bg values in healthkit
|
||||
case speak
|
||||
/// info
|
||||
case info
|
||||
/// developper settings
|
||||
case developer
|
||||
}
|
||||
|
||||
// MARK: - UITableViewDataSource protocol Methods
|
||||
|
@ -153,6 +159,10 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
return alarmsSettingsViewModel.sectionTitle()
|
||||
case .speak:
|
||||
return speakSettingsViewModel.sectionTitle()
|
||||
case .developer:
|
||||
return developmentSettingsViewModel.sectionTitle()
|
||||
case .info:
|
||||
return infoSettingsViewModel.sectionTitle()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,6 +187,11 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
return alarmsSettingsViewModel.numberOfRows()
|
||||
case .speak:
|
||||
return speakSettingsViewModel.numberOfRows()
|
||||
case .developer:
|
||||
return developmentSettingsViewModel.numberOfRows()
|
||||
case .info:
|
||||
return infoSettingsViewModel.numberOfRows()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,6 +216,10 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
viewModel = alarmsSettingsViewModel
|
||||
case .speak:
|
||||
viewModel = speakSettingsViewModel
|
||||
case .developer:
|
||||
viewModel = developmentSettingsViewModel
|
||||
case .info:
|
||||
viewModel = infoSettingsViewModel
|
||||
}
|
||||
|
||||
|
||||
|
@ -226,6 +245,8 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
cell.selectionStyle = .gray
|
||||
case .none:
|
||||
cell.selectionStyle = .none
|
||||
@unknown default:
|
||||
cell.selectionStyle = .none
|
||||
}
|
||||
|
||||
cell.accessoryView = viewModel.uiView(index: indexPath.row)
|
||||
|
@ -287,6 +308,10 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
viewModel = alarmsSettingsViewModel
|
||||
case .speak:
|
||||
viewModel = speakSettingsViewModel
|
||||
case .developer:
|
||||
viewModel = developmentSettingsViewModel
|
||||
case .info:
|
||||
viewModel = infoSettingsViewModel
|
||||
}
|
||||
|
||||
if let viewModel = viewModel {
|
||||
|
@ -349,6 +374,11 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
|
||||
case .performSegue(let withIdentifier):
|
||||
self.performSegue(withIdentifier: withIdentifier, sender: nil)
|
||||
|
||||
case let .showInfoText(title, message):
|
||||
|
||||
UIAlertController(title: title, message: message, actionHandler: nil).presentInOwnWindow(animated: true, completion: nil)
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -356,6 +386,14 @@ extension SettingsViewController:UITableViewDataSource, UITableViewDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
|
||||
// apple doc says : Use this method to respond to taps in the detail button accessory view of a row. The table view does not call this method for other types of accessory views.
|
||||
// when user clicks on of the detail buttons, then consider this as row selected, for now - as it's only license that is using this button for now
|
||||
self.tableView(tableView, didSelectRowAt: indexPath)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// defines perform segue identifiers used within settingsviewcontroller
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
import UIKit
|
||||
|
||||
fileprivate enum Setting:Int, CaseIterable {
|
||||
|
||||
// for G6 testing, factor 1
|
||||
case G6v2ScalingFactor1 = 0
|
||||
|
||||
// for G6 testing, factor 2
|
||||
case G6v2ScalingFactor2 = 1
|
||||
|
||||
}
|
||||
|
||||
struct SettingsViewDevelopmentSettingsViewModel:SettingsViewModelProtocol {
|
||||
|
||||
func sectionTitle() -> String? {
|
||||
return "Developer Settings"
|
||||
}
|
||||
|
||||
func settingsRowText(index: Int) -> String {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .G6v2ScalingFactor1:
|
||||
return "G6 v2 scaling factor 1"
|
||||
case .G6v2ScalingFactor2:
|
||||
return "G6 v2 scaling factor 2"
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func accessoryType(index: Int) -> UITableViewCell.AccessoryType {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .G6v2ScalingFactor1:
|
||||
return UITableViewCell.AccessoryType.disclosureIndicator
|
||||
|
||||
case .G6v2ScalingFactor2:
|
||||
return UITableViewCell.AccessoryType.disclosureIndicator
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func detailedText(index: Int) -> String? {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .G6v2ScalingFactor1:
|
||||
if let factor = UserDefaults.standard.G6v2ScalingFactor1 {
|
||||
return factor
|
||||
} else {
|
||||
return CGMG6Transmitter.G6v2DefaultScalingFactor1.description
|
||||
}
|
||||
case .G6v2ScalingFactor2:
|
||||
if let factor = UserDefaults.standard.G6v2ScalingFactor2 {
|
||||
return factor
|
||||
} else {
|
||||
return CGMG6Transmitter.G6v2DefaultScalingFactor2.description
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func uiView(index: Int) -> UIView? {
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func numberOfRows() -> Int {
|
||||
return Setting.allCases.count
|
||||
}
|
||||
|
||||
func onRowSelect(index: Int) -> SettingsSelectedRowAction {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .G6v2ScalingFactor1:
|
||||
|
||||
return SettingsSelectedRowAction.askText(title: "G6 scaling", message: "Give G6 v2 scaling factor 1", keyboardType: UIKeyboardType.decimalPad, text: UserDefaults.standard.G6v2ScalingFactor1, placeHolder: CGMG6Transmitter.G6v2DefaultScalingFactor1.description, actionTitle: nil, cancelTitle: nil, actionHandler: {(factor:String) in
|
||||
|
||||
// convert to uppercase
|
||||
if let factorAsDouble = factor.toDouble() {
|
||||
UserDefaults.standard.G6v2ScalingFactor1 = factorAsDouble.description
|
||||
}
|
||||
|
||||
}, cancelHandler: nil)
|
||||
|
||||
case .G6v2ScalingFactor2:
|
||||
|
||||
return SettingsSelectedRowAction.askText(title: "G6 scaling", message: "Give G6 v2 scaling factor 2", keyboardType: UIKeyboardType.decimalPad, text: UserDefaults.standard.G6v2ScalingFactor2, placeHolder: CGMG6Transmitter.G6v2DefaultScalingFactor2.description, actionTitle: nil, cancelTitle: nil, actionHandler: {(factor:String) in
|
||||
|
||||
// convert to uppercase
|
||||
if let factorAsDouble = factor.toDouble() {
|
||||
UserDefaults.standard.G6v2ScalingFactor2 = factorAsDouble.description
|
||||
}
|
||||
|
||||
|
||||
}, cancelHandler: nil)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func isEnabled(index: Int) -> Bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func completeSettingsViewRefreshNeeded(index: Int) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -33,10 +33,10 @@ struct SettingsViewGeneralSettingsViewModel:SettingsViewModelProtocol {
|
|||
case .bloodGlucoseUnit:
|
||||
return SettingsSelectedRowAction.callFunction(function: {UserDefaults.standard.bloodGlucoseUnitIsMgDl ? (UserDefaults.standard.bloodGlucoseUnitIsMgDl) = false : (UserDefaults.standard.bloodGlucoseUnitIsMgDl = true)})
|
||||
case .lowMarkValue:
|
||||
return SettingsSelectedRowAction.askText(title: Texts_SettingsView.labelLowValue, message: nil, keyboardType: UserDefaults.standard.bloodGlucoseUnitIsMgDl ? .numberPad:.decimalPad, text: UserDefaults.standard.lowMarkValueInUserChosenUnitRounded, placeHolder: Constants.BGGraphBuilder.defaultLowMarkInMgdl.description, actionTitle: nil, cancelTitle: nil, actionHandler: {(lowMarkValue:String) in UserDefaults.standard.lowMarkValueInUserChosenUnitRounded = lowMarkValue}, cancelHandler: nil)
|
||||
return SettingsSelectedRowAction.askText(title: Texts_SettingsView.labelLowValue, message: nil, keyboardType: UserDefaults.standard.bloodGlucoseUnitIsMgDl ? .numberPad:.decimalPad, text: UserDefaults.standard.lowMarkValueInUserChosenUnitRounded, placeHolder: ConstantsBGGraphBuilder.defaultLowMarkInMgdl.description, actionTitle: nil, cancelTitle: nil, actionHandler: {(lowMarkValue:String) in UserDefaults.standard.lowMarkValueInUserChosenUnitRounded = lowMarkValue}, cancelHandler: nil)
|
||||
|
||||
case .highMarkValue:
|
||||
return SettingsSelectedRowAction.askText(title: Texts_SettingsView.labelHighValue, message: nil, keyboardType: UserDefaults.standard.bloodGlucoseUnitIsMgDl ? .numberPad:.decimalPad, text: UserDefaults.standard.highMarkValueInUserChosenUnitRounded, placeHolder: Constants.BGGraphBuilder.defaultHighMmarkInMgdl.description, actionTitle: nil, cancelTitle: nil, actionHandler: {(highMarkValue:String) in UserDefaults.standard.highMarkValueInUserChosenUnitRounded = highMarkValue}, cancelHandler: nil)
|
||||
return SettingsSelectedRowAction.askText(title: Texts_SettingsView.labelHighValue, message: nil, keyboardType: UserDefaults.standard.bloodGlucoseUnitIsMgDl ? .numberPad:.decimalPad, text: UserDefaults.standard.highMarkValueInUserChosenUnitRounded, placeHolder: ConstantsBGGraphBuilder.defaultHighMmarkInMgdl.description, actionTitle: nil, cancelTitle: nil, actionHandler: {(highMarkValue:String) in UserDefaults.standard.highMarkValueInUserChosenUnitRounded = highMarkValue}, cancelHandler: nil)
|
||||
case .masterFollower:
|
||||
|
||||
return SettingsSelectedRowAction.callFunction(function: {
|
||||
|
|
|
@ -8,7 +8,7 @@ class SettingsViewHealthKitSettingsViewModel:SettingsViewModelProtocol {
|
|||
// MARK: - private properties
|
||||
|
||||
/// for logging
|
||||
private var log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryHealthKitManager)
|
||||
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryHealthKitManager)
|
||||
|
||||
// MARK: - functions in protocol SettingsViewModelProtocol
|
||||
|
||||
|
@ -75,6 +75,8 @@ class SettingsViewHealthKitSettingsViewModel:SettingsViewModelProtocol {
|
|||
os_log("user removed authorization to store bgreadings in healthkit", log: self.log, type: .error)
|
||||
case .sharingAuthorized:
|
||||
break
|
||||
@unknown default:
|
||||
os_log("unknown authorizationstatus for healthkit - SettingsViewHealthKitSettingsViewModel", log: self.log, type: .error)
|
||||
}
|
||||
} else {
|
||||
os_log("User enabled HealthKit however failed to create bloodGlucoseType", log: self.log, type: .error)
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
import UIKit
|
||||
|
||||
fileprivate enum Setting:Int, CaseIterable {
|
||||
|
||||
/// version Number
|
||||
case versionNumber = 0
|
||||
|
||||
/// licenseInfo
|
||||
case licenseInfo = 1
|
||||
|
||||
}
|
||||
|
||||
struct SettingsViewInfoViewModel:SettingsViewModelProtocol {
|
||||
|
||||
func sectionTitle() -> String? {
|
||||
return Texts_HomeView.info
|
||||
}
|
||||
|
||||
func settingsRowText(index: Int) -> String {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .versionNumber:
|
||||
|
||||
return Texts_SettingsView.version
|
||||
|
||||
case .licenseInfo:
|
||||
return Texts_SettingsView.license
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func accessoryType(index: Int) -> UITableViewCell.AccessoryType {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .versionNumber:
|
||||
return UITableViewCell.AccessoryType.none
|
||||
|
||||
case .licenseInfo:
|
||||
return UITableViewCell.AccessoryType.detailButton
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func detailedText(index: Int) -> String? {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .versionNumber:
|
||||
|
||||
guard let dictionary = Bundle.main.infoDictionary else {return "unknown"}
|
||||
|
||||
guard let version = dictionary["CFBundleShortVersionString"] as? String else {return "unknown"}
|
||||
|
||||
return version
|
||||
|
||||
case .licenseInfo:
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func uiView(index: Int) -> UIView? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func numberOfRows() -> Int {
|
||||
return Setting.allCases.count
|
||||
}
|
||||
|
||||
func onRowSelect(index: Int) -> SettingsSelectedRowAction {
|
||||
|
||||
guard let setting = Setting(rawValue: index) else { fatalError("Unexpected Section") }
|
||||
|
||||
switch setting {
|
||||
|
||||
case .versionNumber:
|
||||
return SettingsSelectedRowAction.nothing
|
||||
|
||||
case .licenseInfo:
|
||||
|
||||
return SettingsSelectedRowAction.showInfoText(title: ConstantsHomeView.applicationName, message: Texts_HomeView.licenseInfo + ConstantsHomeView.infoEmailAddress)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func isEnabled(index: Int) -> Bool {
|
||||
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
func completeSettingsViewRefreshNeeded(index: Int) -> Bool {
|
||||
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -49,14 +49,14 @@ class SettingsViewSpeakSettingsViewModel:SettingsViewModelProtocol {
|
|||
//find index for languageCode type currently stored in userdefaults
|
||||
var selectedRow:Int?
|
||||
if let languageCode = UserDefaults.standard.speakReadingLanguageCode {
|
||||
selectedRow = Constants.SpeakReadingLanguages.allLanguageNamesAndCodes.codes.index(of:languageCode)
|
||||
selectedRow = ConstantsSpeakReadingLanguages.allLanguageNamesAndCodes.codes.firstIndex(of:languageCode)
|
||||
} else {
|
||||
selectedRow = Constants.SpeakReadingLanguages.allLanguageNamesAndCodes.codes.index(of:Texts_SpeakReading.defaultLanguageCode)
|
||||
selectedRow = ConstantsSpeakReadingLanguages.allLanguageNamesAndCodes.codes.firstIndex(of:Texts_SpeakReading.defaultLanguageCode)
|
||||
}
|
||||
|
||||
return SettingsSelectedRowAction.selectFromList(title: Texts_SettingsView.speakReadingLanguageSelection, data: Constants.SpeakReadingLanguages.allLanguageNamesAndCodes.names, selectedRow: selectedRow, actionTitle: nil, cancelTitle: nil, actionHandler: {(index:Int) in
|
||||
return SettingsSelectedRowAction.selectFromList(title: Texts_SettingsView.speakReadingLanguageSelection, data: ConstantsSpeakReadingLanguages.allLanguageNamesAndCodes.names, selectedRow: selectedRow, actionTitle: nil, cancelTitle: nil, actionHandler: {(index:Int) in
|
||||
if index != selectedRow {
|
||||
UserDefaults.standard.speakReadingLanguageCode = Constants.SpeakReadingLanguages.allLanguageNamesAndCodes.codes[index]
|
||||
UserDefaults.standard.speakReadingLanguageCode = ConstantsSpeakReadingLanguages.allLanguageNamesAndCodes.codes[index]
|
||||
}
|
||||
}, cancelHandler: nil, didSelectRowHandler: nil)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
|
|||
//find index for transmitter type currently stored in userdefaults
|
||||
var selectedRow:Int?
|
||||
if let transmitterType = UserDefaults.standard.transmitterType?.rawValue {
|
||||
selectedRow = data.index(of:transmitterType)
|
||||
selectedRow = data.firstIndex(of:transmitterType)
|
||||
}
|
||||
|
||||
return SettingsSelectedRowAction.selectFromList(title: Texts_SettingsView.labelTransmitterId, data: data, selectedRow: selectedRow, actionTitle: nil, cancelTitle: nil, actionHandler: {(index:Int) in
|
||||
|
|
Loading…
Reference in New Issue