redesign M5Stack stuff, as preperation to move all bluetooth stuff to that page

This commit is contained in:
Johan Degraeve 2019-11-28 17:18:06 +01:00
parent bfdbd281a7
commit 4248d359e1
67 changed files with 2623 additions and 2242 deletions

View File

@ -10,7 +10,6 @@
A48D2DE552F4A356AA32746A /* Pods_xdrip.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 662BEA7F7991B9BD2E7D3EA4 /* Pods_xdrip.framework */; };
EE9947E822EEDD2E00DCB876 /* CGMBubbleTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9947E722EEDD2E00DCB876 /* CGMBubbleTransmitter.swift */; };
F8025C0A21D94FD700ECF0C0 /* CBManagerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8025C0921D94FD700ECF0C0 /* CBManagerState.swift */; };
F8025C1121DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8025C1021DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift */; };
F8025C1321DA683400ECF0C0 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8025C1221DA683400ECF0C0 /* Data.swift */; };
F8025E4E21ED450300ECF0C0 /* Double.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8025E4D21ED450300ECF0C0 /* Double.swift */; };
F8025E5021EE746400ECF0C0 /* Calibrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8025E4F21EE746400ECF0C0 /* Calibrator.swift */; };
@ -25,7 +24,6 @@
F80ED2EC236F68F90005C035 /* SettingsViewM5StackBluetoothSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80ED2E9236F68F90005C035 /* SettingsViewM5StackBluetoothSettingsViewModel.swift */; };
F80ED2ED236F68F90005C035 /* SettingsViewM5StackGeneralSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80ED2EA236F68F90005C035 /* SettingsViewM5StackGeneralSettingsViewModel.swift */; };
F80ED2EE236F68F90005C035 /* SettingsViewM5StackWiFiSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80ED2EB236F68F90005C035 /* SettingsViewM5StackWiFiSettingsViewModel.swift */; };
F80ED2F32379C6CD0005C035 /* M5StackBluetoothDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F80ED2F22379C6CD0005C035 /* M5StackBluetoothDelegate.swift */; };
F81D6D4822BD5F62005EFAE2 /* DexcomShareUploadManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81D6D4722BD5F62005EFAE2 /* DexcomShareUploadManager.swift */; };
F81D6D4E22BFC762005EFAE2 /* TextsDexcomShareTestResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81D6D4D22BFC762005EFAE2 /* TextsDexcomShareTestResult.swift */; };
F81D6D5222C27F18005EFAE2 /* BgReading+DexcomShare.swift in Sources */ = {isa = PBXBuildFile; fileRef = F81D6D5122C27F18005EFAE2 /* BgReading+DexcomShare.swift */; };
@ -55,11 +53,19 @@
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 */; };
F8297F45238DC4AC00D74D66 /* BluetoothPerpheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F43238DC4AC00D74D66 /* BluetoothPerpheralManager.swift */; };
F8297F46238DC4AC00D74D66 /* BluetoothPeripheralManaging.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F44238DC4AC00D74D66 /* BluetoothPeripheralManaging.swift */; };
F8297F4E238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F4B238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift */; };
F8297F4F238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F4C238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift */; };
F8297F52238ECA3200D74D66 /* BluetoothPeripheralViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F51238ECA3200D74D66 /* BluetoothPeripheralViewController.swift */; };
F8297F56238ED07700D74D66 /* BluetoothPeripheralManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F55238ED07700D74D66 /* BluetoothPeripheralManager.swift */; };
F8297F59238EE14E00D74D66 /* TextsBluetoothPeripheralsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F57238EE14E00D74D66 /* TextsBluetoothPeripheralsView.swift */; };
F8297F5A238EE14E00D74D66 /* TextsBluetoothPeripheralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F58238EE14E00D74D66 /* TextsBluetoothPeripheralView.swift */; };
F8297F5C238FC53100D74D66 /* M5Stack+BluetoothPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F5B238FC53100D74D66 /* M5Stack+BluetoothPeripheral.swift */; };
F8297F5E238FC77C00D74D66 /* BluetoothPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F5D238FC77C00D74D66 /* BluetoothPeripheral.swift */; };
F8297F6023902FC300D74D66 /* BluetoothPeripheralType.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F5F23902FC300D74D66 /* BluetoothPeripheralType.swift */; };
F8297F62239267F900D74D66 /* BluetoothTransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F61239267F900D74D66 /* BluetoothTransmitterDelegate.swift */; };
F8297F6623942D4200D74D66 /* M5StackBluetoothTransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8297F6523942D4200D74D66 /* M5StackBluetoothTransmitterDelegate.swift */; };
F8365EBC239868A600B0501B /* M5StackBluetoothPeripheralViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8365EBB239868A600B0501B /* M5StackBluetoothPeripheralViewModel.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 */; };
@ -67,20 +73,20 @@
F85DC2F421CFE3D400B9F74A /* Sensor+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2F121CFE3D400B9F74A /* Sensor+CoreDataClass.swift */; };
F85DC2F521CFE3D400B9F74A /* BgReading+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F85DC2F221CFE3D400B9F74A /* BgReading+CoreDataClass.swift */; };
F867E2612252ADAB000FD265 /* Calibration+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F867E25D2252ADAB000FD265 /* Calibration+CoreDataProperties.swift */; };
F8691888239CEEFA0065B607 /* BluetoothPeripheralViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8691887239CEEFA0065B607 /* BluetoothPeripheralViewModel.swift */; };
F869188A23A043890065B607 /* BluetoothPeripheralsView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F869188923A043880065B607 /* BluetoothPeripheralsView.strings */; };
F869188C23A044340065B607 /* TextsM5StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F869188B23A044340065B607 /* TextsM5StackView.swift */; };
F869189023A155100065B607 /* BluetoothPeripheralView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F869188F23A155100065B607 /* BluetoothPeripheralView.strings */; };
F889CB60236C3DCB00A81068 /* CGMBlueReaderTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F889CB5F236C3DCB00A81068 /* CGMBlueReaderTransmitter.swift */; };
F889CB61236D844600A81068 /* M5StacksView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F889CB63236D844600A81068 /* M5StacksView.strings */; };
F889CB6F236D84AC00A81068 /* M5StackView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F889CB71236D84AC00A81068 /* M5StackView.strings */; };
F889CB7F236D850600A81068 /* DexcomShareTestResults.strings in Resources */ = {isa = PBXBuildFile; fileRef = F889CB81236D850600A81068 /* DexcomShareTestResults.strings */; };
F897AAF92200F2D200CDDD10 /* CBPeripheralState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897AAF82200F2D200CDDD10 /* CBPeripheralState.swift */; };
F897AAFB2201018800CDDD10 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = F897AAFA2201018800CDDD10 /* String.swift */; };
F898EDEA233F53BF00BFB79B /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDE9233F53BF00BFB79B /* UIButton.swift */; };
F898EDEC233F549100BFB79B /* UIBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDEB233F549100BFB79B /* UIBarButtonItem.swift */; };
F898EDEE233F852200BFB79B /* M5StackNameAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDED233F852200BFB79B /* M5StackNameAccessor.swift */; };
F898EDF2234A8A0500BFB79B /* UInt8.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDF1234A8A0500BFB79B /* UInt8.swift */; };
F898EDF4234A8A3200BFB79B /* UInt16.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDF3234A8A3200BFB79B /* UInt16.swift */; };
F898EDF6234A8A5700BFB79B /* UInt32.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDF5234A8A5700BFB79B /* UInt32.swift */; };
F898EDF8234F8EDF00BFB79B /* M5StackName+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDF7234F8EDE00BFB79B /* M5StackName+CoreDataProperties.swift */; };
F898EDFA234F8EE500BFB79B /* M5StackName+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = F898EDF9234F8EE500BFB79B /* M5StackName+CoreDataClass.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 */; };
@ -237,8 +243,6 @@
F8EEDD552300136F00D2D610 /* DexcomShareTestResult.strings in Resources */ = {isa = PBXBuildFile; fileRef = F8EEDD572300136F00D2D610 /* DexcomShareTestResult.strings */; };
F8EEDD6423020FAD00D2D610 /* NoCalibrator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8EEDD6323020FAD00D2D610 /* NoCalibrator.swift */; };
F8F62270233AA3B200BE8796 /* M5StackAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F6226F233AA3B200BE8796 /* M5StackAccessor.swift */; };
F8F62276233D1CE600BE8796 /* TextsM5StacksView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F62275233D1CE600BE8796 /* TextsM5StacksView.swift */; };
F8F62278233D1CED00BE8796 /* TextsM5StackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8F62277233D1CED00BE8796 /* TextsM5StackView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@ -247,7 +251,6 @@
E2648F65F347D56D7DFFFAB7 /* Pods-xdrip.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-xdrip.release.xcconfig"; path = "Target Support Files/Pods-xdrip/Pods-xdrip.release.xcconfig"; sourceTree = "<group>"; };
EE9947E722EEDD2E00DCB876 /* CGMBubbleTransmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMBubbleTransmitter.swift; sourceTree = "<group>"; };
F8025C0921D94FD700ECF0C0 /* CBManagerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CBManagerState.swift; sourceTree = "<group>"; };
F8025C1021DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothTransmitterDelegate.swift; sourceTree = "<group>"; };
F8025C1221DA683400ECF0C0 /* Data.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
F8025E4D21ED450300ECF0C0 /* Double.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Double.swift; sourceTree = "<group>"; };
F8025E4F21EE746400ECF0C0 /* Calibrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Calibrator.swift; sourceTree = "<group>"; };
@ -262,7 +265,6 @@
F80ED2E9236F68F90005C035 /* SettingsViewM5StackBluetoothSettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewM5StackBluetoothSettingsViewModel.swift; sourceTree = "<group>"; };
F80ED2EA236F68F90005C035 /* SettingsViewM5StackGeneralSettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewM5StackGeneralSettingsViewModel.swift; sourceTree = "<group>"; };
F80ED2EB236F68F90005C035 /* SettingsViewM5StackWiFiSettingsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsViewM5StackWiFiSettingsViewModel.swift; sourceTree = "<group>"; };
F80ED2F22379C6CD0005C035 /* M5StackBluetoothDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = M5StackBluetoothDelegate.swift; sourceTree = "<group>"; };
F81D6D4522B67F55005EFAE2 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/SpeakReading.strings; sourceTree = "<group>"; };
F81D6D4722BD5F62005EFAE2 /* DexcomShareUploadManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DexcomShareUploadManager.swift; sourceTree = "<group>"; };
F81D6D4D22BFC762005EFAE2 /* TextsDexcomShareTestResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsDexcomShareTestResult.swift; sourceTree = "<group>"; };
@ -295,11 +297,19 @@
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>"; };
F8297F41238C3A6400D74D66 /* xdrip v5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "xdrip v5.xcdatamodel"; sourceTree = "<group>"; };
F8297F43238DC4AC00D74D66 /* BluetoothPerpheralManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPerpheralManager.swift; sourceTree = "<group>"; };
F8297F44238DC4AC00D74D66 /* BluetoothPeripheralManaging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralManaging.swift; sourceTree = "<group>"; };
F8297F4B238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralsViewController.swift; sourceTree = "<group>"; };
F8297F4C238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralNavigationController.swift; sourceTree = "<group>"; };
F8297F51238ECA3200D74D66 /* BluetoothPeripheralViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralViewController.swift; sourceTree = "<group>"; };
F8297F55238ED07700D74D66 /* BluetoothPeripheralManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralManager.swift; sourceTree = "<group>"; };
F8297F57238EE14E00D74D66 /* TextsBluetoothPeripheralsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextsBluetoothPeripheralsView.swift; sourceTree = "<group>"; };
F8297F58238EE14E00D74D66 /* TextsBluetoothPeripheralView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextsBluetoothPeripheralView.swift; sourceTree = "<group>"; };
F8297F5B238FC53100D74D66 /* M5Stack+BluetoothPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "M5Stack+BluetoothPeripheral.swift"; sourceTree = "<group>"; };
F8297F5D238FC77C00D74D66 /* BluetoothPeripheral.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheral.swift; sourceTree = "<group>"; };
F8297F5F23902FC300D74D66 /* BluetoothPeripheralType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralType.swift; sourceTree = "<group>"; };
F8297F61239267F900D74D66 /* BluetoothTransmitterDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothTransmitterDelegate.swift; sourceTree = "<group>"; };
F8297F6523942D4200D74D66 /* M5StackBluetoothTransmitterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackBluetoothTransmitterDelegate.swift; sourceTree = "<group>"; };
F8365EBB239868A600B0501B /* M5StackBluetoothPeripheralViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackBluetoothPeripheralViewModel.swift; sourceTree = "<group>"; };
F846CDD523046BAC00DCF016 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/SettingsViews.strings"; sourceTree = "<group>"; };
F846CDD623046BAE00DCF016 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/SettingsViews.strings; sourceTree = "<group>"; };
F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsBluetoothPairing.swift; sourceTree = "<group>"; };
@ -310,31 +320,12 @@
F85DC2F121CFE3D400B9F74A /* Sensor+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Sensor+CoreDataClass.swift"; sourceTree = "<group>"; };
F85DC2F221CFE3D400B9F74A /* BgReading+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BgReading+CoreDataClass.swift"; sourceTree = "<group>"; };
F867E25D2252ADAB000FD265 /* Calibration+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "Calibration+CoreDataProperties.swift"; path = "xdrip/Core Data/extensions/Calibration+CoreDataProperties.swift"; sourceTree = SOURCE_ROOT; };
F8691887239CEEFA0065B607 /* BluetoothPeripheralViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BluetoothPeripheralViewModel.swift; sourceTree = "<group>"; };
F869188923A043880065B607 /* BluetoothPeripheralsView.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = BluetoothPeripheralsView.strings; sourceTree = "<group>"; };
F869188B23A044340065B607 /* TextsM5StackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsM5StackView.swift; sourceTree = "<group>"; };
F869188F23A155100065B607 /* BluetoothPeripheralView.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = BluetoothPeripheralView.strings; sourceTree = "<group>"; };
F889CB5F236C3DCB00A81068 /* CGMBlueReaderTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMBlueReaderTransmitter.swift; sourceTree = "<group>"; };
F889CB62236D844600A81068 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB64236D849200A81068 /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB65236D849400A81068 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB66236D849500A81068 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB67236D849700A81068 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB68236D849800A81068 /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pl-PL"; path = "pl-PL.lproj/M5StacksView.strings"; sourceTree = "<group>"; };
F889CB69236D849900A81068 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB6A236D849B00A81068 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/M5StacksView.strings"; sourceTree = "<group>"; };
F889CB6B236D849C00A81068 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB6C236D849D00A81068 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB6D236D849E00A81068 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/M5StacksView.strings"; sourceTree = "<group>"; };
F889CB6E236D849F00A81068 /* es-ES */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-ES"; path = "es-ES.lproj/M5StacksView.strings"; sourceTree = "<group>"; };
F889CB70236D84AC00A81068 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB72236D84B000A81068 /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB73236D84B100A81068 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB74236D84B200A81068 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB75236D84B300A81068 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB76236D84B400A81068 /* pl-PL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pl-PL"; path = "pl-PL.lproj/M5StackView.strings"; sourceTree = "<group>"; };
F889CB77236D84B500A81068 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB78236D84B600A81068 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB79236D84B800A81068 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/M5StackView.strings"; sourceTree = "<group>"; };
F889CB7A236D84B900A81068 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB7B236D84BA00A81068 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/M5StackView.strings"; sourceTree = "<group>"; };
F889CB7C236D84BC00A81068 /* es-ES */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-ES"; path = "es-ES.lproj/M5StackView.strings"; sourceTree = "<group>"; };
F889CB80236D850600A81068 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/DexcomShareTestResults.strings; sourceTree = "<group>"; };
F889CB82236D850C00A81068 /* zh */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh; path = zh.lproj/DexcomShareTestResults.strings; sourceTree = "<group>"; };
F889CB83236D851000A81068 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/DexcomShareTestResults.strings; sourceTree = "<group>"; };
@ -358,8 +349,6 @@
F889CB95236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/ErrorMessages.strings; sourceTree = "<group>"; };
F889CB96236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/HomeView.strings; sourceTree = "<group>"; };
F889CB97236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
F889CB98236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/M5StacksView.strings; sourceTree = "<group>"; };
F889CB99236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/M5StackView.strings; sourceTree = "<group>"; };
F889CB9A236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/NightScoutTestResult.strings; sourceTree = "<group>"; };
F889CB9B236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SettingsViews.strings; sourceTree = "<group>"; };
F889CB9C236D89C800A81068 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/SpeakReading.strings; sourceTree = "<group>"; };
@ -368,13 +357,10 @@
F897AAFA2201018800CDDD10 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
F898EDE9233F53BF00BFB79B /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = "<group>"; };
F898EDEB233F549100BFB79B /* UIBarButtonItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIBarButtonItem.swift; sourceTree = "<group>"; };
F898EDED233F852200BFB79B /* M5StackNameAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackNameAccessor.swift; sourceTree = "<group>"; };
F898EDF0234A494C00BFB79B /* xdrip v3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "xdrip v3.xcdatamodel"; sourceTree = "<group>"; };
F898EDF1234A8A0500BFB79B /* UInt8.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UInt8.swift; sourceTree = "<group>"; };
F898EDF3234A8A3200BFB79B /* UInt16.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UInt16.swift; sourceTree = "<group>"; };
F898EDF5234A8A5700BFB79B /* UInt32.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UInt32.swift; sourceTree = "<group>"; };
F898EDF7234F8EDE00BFB79B /* M5StackName+CoreDataProperties.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "M5StackName+CoreDataProperties.swift"; sourceTree = "<group>"; };
F898EDF9234F8EE500BFB79B /* M5StackName+CoreDataClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "M5StackName+CoreDataClass.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>"; };
@ -636,6 +622,7 @@
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>"; };
F8EBB030239701DA0058B0D4 /* xdrip v6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "xdrip v6.xcdatamodel"; sourceTree = "<group>"; };
F8EEDD3D22FD80A500D2D610 /* LibreOOPResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibreOOPResponse.swift; sourceTree = "<group>"; };
F8EEDD3E22FD80A600D2D610 /* LibreOOPClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibreOOPClient.swift; sourceTree = "<group>"; };
F8EEDD3F22FD80A600D2D610 /* LibreMeasurement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LibreMeasurement.swift; sourceTree = "<group>"; };
@ -658,8 +645,6 @@
F8EEDD622300139A00D2D610 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/DexcomShareTestResult.strings; sourceTree = "<group>"; };
F8EEDD6323020FAD00D2D610 /* NoCalibrator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoCalibrator.swift; sourceTree = "<group>"; };
F8F6226F233AA3B200BE8796 /* M5StackAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackAccessor.swift; sourceTree = "<group>"; };
F8F62275233D1CE600BE8796 /* TextsM5StacksView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextsM5StacksView.swift; sourceTree = "<group>"; };
F8F62277233D1CED00BE8796 /* TextsM5StackView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextsM5StackView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -869,7 +854,7 @@
F8297F42238DC4AC00D74D66 /* BluetoothPeripheral */ = {
isa = PBXGroup;
children = (
F8297F43238DC4AC00D74D66 /* BluetoothPerpheralManager.swift */,
F8297F55238ED07700D74D66 /* BluetoothPeripheralManager.swift */,
F8297F44238DC4AC00D74D66 /* BluetoothPeripheralManaging.swift */,
);
path = BluetoothPeripheral;
@ -896,7 +881,9 @@
F8297F50238ECA3200D74D66 /* BluetoothPeripheralViewController */ = {
isa = PBXGroup;
children = (
F8691887239CEEFA0065B607 /* BluetoothPeripheralViewModel.swift */,
F8297F51238ECA3200D74D66 /* BluetoothPeripheralViewController.swift */,
F8365EBB239868A600B0501B /* M5StackBluetoothPeripheralViewModel.swift */,
);
path = BluetoothPeripheralViewController;
sourceTree = "<group>";
@ -927,22 +914,23 @@
F85DC2F921D2CCC000B9F74A /* Storyboards */ = {
isa = PBXGroup;
children = (
F889CB81236D850600A81068 /* DexcomShareTestResults.strings */,
F8B3A7B3226A0A71004BA588 /* Alerts.strings */,
F8B3A80B227A3E97004BA588 /* AlertTypesSettingsView.strings */,
F869188923A043880065B607 /* BluetoothPeripheralsView.strings */,
F8B48AA222B2FA9A009BCC01 /* CalibrationRequest.strings */,
F8BDD444221C9D0D006EAB84 /* Common.strings */,
F8EEDD572300136F00D2D610 /* DexcomShareTestResult.strings */,
F889CB81236D850600A81068 /* DexcomShareTestResults.strings */,
F8BDD44A221C9D70006EAB84 /* ErrorMessages.strings */,
F8B48A9E22B2FA7B009BCC01 /* HomeView.strings */,
F8AC426821ADEBD70078C348 /* LaunchScreen.storyboard */,
F8BDD436221A0349006EAB84 /* Localizable.strings */,
F889CB63236D844600A81068 /* M5StacksView.strings */,
F889CB71236D84AC00A81068 /* M5StackView.strings */,
F8AC426321ADEBD60078C348 /* Main.storyboard */,
F8B3A788225D4473004BA588 /* NightScoutTestResult.strings */,
F8BDD457221DEF22006EAB84 /* SettingsViews.strings */,
F8B48A9A22B2FA66009BCC01 /* SpeakReading.strings */,
F869188F23A155100065B607 /* BluetoothPeripheralView.strings */,
);
path = Storyboards;
sourceTree = "<group>";
@ -979,8 +967,10 @@
F85DC2FF21D3E83100B9F74A /* GenericBluetoothTransmitter */ = {
isa = PBXGroup;
children = (
F8297F61239267F900D74D66 /* BluetoothTransmitterDelegate.swift */,
F8297F5D238FC77C00D74D66 /* BluetoothPeripheral.swift */,
F8EA6CAC21BC2CA40082976B /* BluetoothTransmitter.swift */,
F8025C1021DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift */,
F8297F5F23902FC300D74D66 /* BluetoothPeripheralType.swift */,
);
path = GenericBluetoothTransmitter;
sourceTree = "<group>";
@ -996,11 +986,12 @@
F8A389B82315DB080010F405 /* M5Stack */ = {
isa = PBXGroup;
children = (
F80ED2F22379C6CD0005C035 /* M5StackBluetoothDelegate.swift */,
F8A389C4231FB9B30010F405 /* Utilities */,
F8A389BD231DC7DC0010F405 /* M5StackMessages */,
F8297F5B238FC53100D74D66 /* M5Stack+BluetoothPeripheral.swift */,
F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */,
F8297F6523942D4200D74D66 /* M5StackBluetoothTransmitterDelegate.swift */,
F8A389BD231DC7DC0010F405 /* M5StackMessages */,
F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */,
F8A389C4231FB9B30010F405 /* Utilities */,
);
path = M5Stack;
sourceTree = "<group>";
@ -1267,7 +1258,6 @@
F8B3A819227DEC92004BA588 /* README.md */,
F8B3A815227DEC91004BA588 /* SensorsAccessor.swift */,
F8F6226F233AA3B200BE8796 /* M5StackAccessor.swift */,
F898EDED233F852200BFB79B /* M5StackNameAccessor.swift */,
);
path = accessors;
sourceTree = "<group>";
@ -1367,6 +1357,8 @@
F8BDD44C221CAA26006EAB84 /* Texts */ = {
isa = PBXGroup;
children = (
F8297F57238EE14E00D74D66 /* TextsBluetoothPeripheralsView.swift */,
F8297F58238EE14E00D74D66 /* TextsBluetoothPeripheralView.swift */,
F8B3A7B1226A0878004BA588 /* TextsAlerts.swift */,
F8B3A809227A3D11004BA588 /* TextsAlertTypeSettings.swift */,
F81FA005228E09D40028C70F /* TextsCalibration.swift */,
@ -1374,11 +1366,10 @@
F81D6D4D22BFC762005EFAE2 /* TextsDexcomShareTestResult.swift */,
F8BDD43E221B5BAF006EAB84 /* TextsErrorMessages.swift */,
F81FA009228F53680028C70F /* TextsHomeView.swift */,
F8F62275233D1CE600BE8796 /* TextsM5StacksView.swift */,
F8F62277233D1CED00BE8796 /* TextsM5StackView.swift */,
F8B3A782225D37F2004BA588 /* TextsNightScoutTestResult.swift */,
F8BDD451221DEAB1006EAB84 /* TextsSettingsView.swift */,
F8B48A9322B2A705009BCC01 /* TextsSpeakReading.swift */,
F869188B23A044340065B607 /* TextsM5StackView.swift */,
);
path = Texts;
sourceTree = "<group>";
@ -1452,8 +1443,6 @@
F8EA6CA421B9A25B0082976B /* classes */ = {
isa = PBXGroup;
children = (
F898EDF9234F8EE500BFB79B /* M5StackName+CoreDataClass.swift */,
F898EDF7234F8EDE00BFB79B /* M5StackName+CoreDataProperties.swift */,
F804870A2336D90200EBDDB7 /* M5Stack+CoreDataClass.swift */,
F804870B2336D90200EBDDB7 /* M5Stack+CoreDataProperties.swift */,
F8B3A79122635A25004BA588 /* AlertEntry+CoreDataClass.swift */,
@ -1564,6 +1553,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
F869189023A155100065B607 /* BluetoothPeripheralView.strings in Resources */,
F8BDD442221C9D0D006EAB84 /* Common.strings in Resources */,
F8AC426A21ADEBD70078C348 /* LaunchScreen.storyboard in Resources */,
F8BDD438221A0349006EAB84 /* Localizable.strings in Resources */,
@ -1585,13 +1575,13 @@
F889CB6F236D84AC00A81068 /* M5StackView.strings in Resources */,
F8B3A80D227A3E98004BA588 /* AlertTypesSettingsView.strings in Resources */,
F8B3A7D3226CC0B7004BA588 /* xdripalert.aif in Resources */,
F889CB61236D844600A81068 /* M5StacksView.strings in Resources */,
F8AC426721ADEBD70078C348 /* Assets.xcassets in Resources */,
F8EEDD552300136F00D2D610 /* DexcomShareTestResult.strings in Resources */,
F8B3A7CF226CC0B7004BA588 /* shorthigh4.mp3 in Resources */,
F821CF7D22A46CDD005C1E43 /* 1-millisecond-of-silence.mp3 in Resources */,
F8B3A7CE226CC0B7004BA588 /* shorthigh2.mp3 in Resources */,
F889CB7F236D850600A81068 /* DexcomShareTestResults.strings in Resources */,
F869188A23A043890065B607 /* BluetoothPeripheralsView.strings in Resources */,
F821CF7F22A4EDCF005C1E43 /* 20ms-of-silence.caf in Resources */,
F8B3A7CC226CC0B7004BA588 /* shorthigh1.mp3 in Resources */,
F8AC426521ADEBD60078C348 /* Main.storyboard in Resources */,
@ -1676,6 +1666,7 @@
F8A54B0122D9179100934E7A /* LibreSensorState.swift in Sources */,
F8EA6C8221B723BC0082976B /* Date.swift in Sources */,
F8A54AE622D911BA00934E7A /* KeepAliveTxMessage.swift in Sources */,
F8297F6023902FC300D74D66 /* BluetoothPeripheralType.swift in Sources */,
F81FA006228E09D40028C70F /* TextsCalibration.swift in Sources */,
F8A54ABA22D9111900934E7A /* GlucoseData.swift in Sources */,
F8B3A84A227F090E004BA588 /* SettingsViewGeneralSettingsViewModel.swift in Sources */,
@ -1690,6 +1681,7 @@
F8B3A853227F2743004BA588 /* AlertsSettingsViewController.swift in Sources */,
F8B3A849227F090E004BA588 /* SettingsViewSpeakSettingsViewModel.swift in Sources */,
F8BDD43F221B5BAF006EAB84 /* TextsErrorMessages.swift in Sources */,
F8365EBC239868A600B0501B /* M5StackBluetoothPeripheralViewModel.swift in Sources */,
F8B3A7FA2278E0E8004BA588 /* SettingsViewModelProtocol.swift in Sources */,
F85DC2F521CFE3D400B9F74A /* BgReading+CoreDataClass.swift in Sources */,
F8A54AEA22D911BA00934E7A /* PairRequestTxMessage.swift in Sources */,
@ -1709,11 +1701,14 @@
F8EA6CA921BBE3010082976B /* UniqueId.swift in Sources */,
F8A1585122EDB597007F5B5D /* ConstantsBGGraphBuilder.swift in Sources */,
F81D6D4822BD5F62005EFAE2 /* DexcomShareUploadManager.swift in Sources */,
F8297F62239267F900D74D66 /* BluetoothTransmitterDelegate.swift in Sources */,
F8A7406E22D9C0E700967CFC /* CGMBluconTransmitter.swift in Sources */,
F8A1586B22EDB967007F5B5D /* ConstantsMaster.swift in Sources */,
F80859292364D61B00F3829D /* UserDefaults+charts.swift in Sources */,
F8A389C3231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift in Sources */,
F8297F5E238FC77C00D74D66 /* BluetoothPeripheral.swift in Sources */,
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
F8297F5C238FC53100D74D66 /* M5Stack+BluetoothPeripheral.swift in Sources */,
F8297F46238DC4AC00D74D66 /* BluetoothPeripheralManaging.swift in Sources */,
F8A54B0022D9179100934E7A /* LibreDataParser.swift in Sources */,
F8025E5421EE8D2100ECF0C0 /* Libre1Calibrator.swift in Sources */,
@ -1728,7 +1723,6 @@
F897AAF92200F2D200CDDD10 /* CBPeripheralState.swift in Sources */,
F8A54AE522D911BA00934E7A /* TransmitterVersionRxMessage.swift in Sources */,
F821CF57229BF43A005C1E43 /* SnoozeParameters.swift in Sources */,
F8F62278233D1CED00BE8796 /* TextsM5StackView.swift in Sources */,
F8A54AFF22D9179100934E7A /* CRC.swift in Sources */,
F8B3A79722635A25004BA588 /* AlertEntry+CoreDataProperties.swift in Sources */,
F80610C4222D4E4D00D8F236 /* ActionClosureable-extension.swift in Sources */,
@ -1736,7 +1730,6 @@
F821CF9522ADB0D7005C1E43 /* HealthKitManager.swift in Sources */,
F8B3A81D227DEC92004BA588 /* CalibrationsAccessor.swift in Sources */,
F8A54AE122D911BA00934E7A /* SensorDataTxMessage.swift in Sources */,
F898EDEE233F852200BFB79B /* M5StackNameAccessor.swift in Sources */,
F821CF9D22AEF483005C1E43 /* BGReadingSpeaker.swift in Sources */,
F8297F4E238DCAD800D74D66 /* BluetoothPeripheralsViewController.swift in Sources */,
F8EEDD4622FD80A600D2D610 /* LibreMeasurement.swift in Sources */,
@ -1752,7 +1745,6 @@
F8A1587322EDC893007F5B5D /* ConstantsDexcomShare.swift in Sources */,
F8A1586F22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift in Sources */,
F8A54AB822D9111900934E7A /* TransmitterBatteryInfo.swift in Sources */,
F8297F45238DC4AC00D74D66 /* BluetoothPerpheralManager.swift in Sources */,
F8B3A82D227F07D6004BA588 /* SettingsNavigationController.swift in Sources */,
F8A54AB722D9111900934E7A /* CGMTransmitter.swift in Sources */,
F80ED2EC236F68F90005C035 /* SettingsViewM5StackBluetoothSettingsViewModel.swift in Sources */,
@ -1776,17 +1768,17 @@
F8AC425E21ADEBD60078C348 /* AppDelegate.swift in Sources */,
F804870D2336D90200EBDDB7 /* M5Stack+CoreDataProperties.swift in Sources */,
F821CF8E22AB090C005C1E43 /* DatePickerViewController.swift in Sources */,
F8691888239CEEFA0065B607 /* BluetoothPeripheralViewModel.swift in Sources */,
F8297F4F238DCAD800D74D66 /* BluetoothPeripheralNavigationController.swift in Sources */,
F898EDF8234F8EDF00BFB79B /* M5StackName+CoreDataProperties.swift in Sources */,
F8A1585322EDB602007F5B5D /* ConstantsBloodGlucose.swift in Sources */,
F80ED2EE236F68F90005C035 /* SettingsViewM5StackWiFiSettingsViewModel.swift in Sources */,
F8A389C823203E3E0010F405 /* ConstantsM5Stack.swift in Sources */,
F898EDEA233F53BF00BFB79B /* UIButton.swift in Sources */,
F8297F6623942D4200D74D66 /* M5StackBluetoothTransmitterDelegate.swift in Sources */,
F8A54AE322D911BA00934E7A /* AuthRequestRxMessage.swift in Sources */,
F8A54AD722D911BA00934E7A /* CGMG6Transmitter.swift in Sources */,
F81F9FF822861E6D0028C70F /* KeyValueObserverTimeKeeper.swift in Sources */,
F8B3A858227F6971004BA588 /* UISwitch.swift in Sources */,
F8F62276233D1CE600BE8796 /* TextsM5StacksView.swift in Sources */,
F8A1586722EDB8BF007F5B5D /* ConstantsHomeView.swift in Sources */,
F8A1585922EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift in Sources */,
F8A54AAD22D6859200934E7A /* SlopeParameters.swift in Sources */,
@ -1795,7 +1787,6 @@
F8B3A80A227A3D11004BA588 /* TextsAlertTypeSettings.swift in Sources */,
F8A1586D22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift in Sources */,
F80ED2ED236F68F90005C035 /* SettingsViewM5StackGeneralSettingsViewModel.swift in Sources */,
F898EDFA234F8EE500BFB79B /* M5StackName+CoreDataClass.swift in Sources */,
F8B3A850227F26F8004BA588 /* AlertTypesSettingsViewController.swift in Sources */,
F8A7407022DBB24800967CFC /* BluconTransmitterOpCode.swift in Sources */,
F8EA6CAD21BC2CA40082976B /* BluetoothTransmitter.swift in Sources */,
@ -1829,17 +1820,17 @@
F8297F52238ECA3200D74D66 /* BluetoothPeripheralViewController.swift in Sources */,
F821CF66229EE68B005C1E43 /* NightScoutFollowManager.swift in Sources */,
F8A54AF622D9156600934E7A /* CGMGNSEntryTransmitter.swift in Sources */,
F869188C23A044340065B607 /* TextsM5StackView.swift in Sources */,
F8A389E7232ECE7E0010F405 /* SettingsViewUtilities.swift in Sources */,
F8B3A7DF226E48C1004BA588 /* SoundPlayer.swift in Sources */,
F8B3A820227DEC92004BA588 /* AlertTypesAccessor.swift in Sources */,
F8B3A81E227DEC92004BA588 /* BgReadingsAccessor.swift in Sources */,
F8A389C6231FBA110010F405 /* M5StackPacket.swift in Sources */,
F8B3A846227F090E004BA588 /* SettingsViewTransmitterSettingsViewModel.swift in Sources */,
F80ED2F32379C6CD0005C035 /* M5StackBluetoothDelegate.swift in Sources */,
F821CF6B229FC22D005C1E43 /* Endpoint.swift in Sources */,
F821CF58229BF43A005C1E43 /* AlertManager.swift in Sources */,
F8A389CC232191280010F405 /* M5StackUtilities.swift in Sources */,
F8025C1121DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift in Sources */,
F8297F59238EE14E00D74D66 /* TextsBluetoothPeripheralsView.swift in Sources */,
F898EDEC233F549100BFB79B /* UIBarButtonItem.swift in Sources */,
F8A54AD922D911BA00934E7A /* TransmitterVersionTxMessage.swift in Sources */,
F8A389CF232AE2EA0010F405 /* M5StackSettingsViewController.swift in Sources */,
@ -1850,6 +1841,8 @@
F8025C1321DA683400ECF0C0 /* Data.swift in Sources */,
F80859272364355F00F3829D /* ConstantsGlucoseChart.swift in Sources */,
F85DC2EF21CFE2F500B9F74A /* Sensor+CoreDataProperties.swift in Sources */,
F8297F56238ED07700D74D66 /* BluetoothPeripheralManager.swift in Sources */,
F8297F5A238EE14E00D74D66 /* TextsBluetoothPeripheralView.swift in Sources */,
F8A54AFA22D9156600934E7A /* CGMMiaoMiaoTransmitter.swift in Sources */,
F8BECB02235CE3E20060DAE1 /* BloodGlucoseChartView.swift in Sources */,
F8A1584D22ECA445007F5B5D /* SettingsViewDevelopmentSettingsViewModel.swift in Sources */,
@ -1869,42 +1862,10 @@
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
F889CB63236D844600A81068 /* M5StacksView.strings */ = {
isa = PBXVariantGroup;
children = (
F889CB62236D844600A81068 /* en */,
F889CB64236D849200A81068 /* zh */,
F889CB65236D849400A81068 /* nl */,
F889CB66236D849500A81068 /* fr */,
F889CB67236D849700A81068 /* it */,
F889CB68236D849800A81068 /* pl-PL */,
F889CB69236D849900A81068 /* pt */,
F889CB6A236D849B00A81068 /* pt-BR */,
F889CB6B236D849C00A81068 /* ru */,
F889CB6C236D849D00A81068 /* sl */,
F889CB6D236D849E00A81068 /* es-MX */,
F889CB6E236D849F00A81068 /* es-ES */,
F889CB98236D89C800A81068 /* de */,
);
name = M5StacksView.strings;
sourceTree = "<group>";
};
F889CB71236D84AC00A81068 /* M5StackView.strings */ = {
isa = PBXVariantGroup;
children = (
F889CB70236D84AC00A81068 /* en */,
F889CB72236D84B000A81068 /* zh */,
F889CB73236D84B100A81068 /* nl */,
F889CB74236D84B200A81068 /* it */,
F889CB75236D84B300A81068 /* fr */,
F889CB76236D84B400A81068 /* pl-PL */,
F889CB77236D84B500A81068 /* pt */,
F889CB78236D84B600A81068 /* ru */,
F889CB79236D84B800A81068 /* pt-BR */,
F889CB7A236D84B900A81068 /* sl */,
F889CB7B236D84BA00A81068 /* es-MX */,
F889CB7C236D84BC00A81068 /* es-ES */,
F889CB99236D89C800A81068 /* de */,
);
name = M5StackView.strings;
sourceTree = "<group>";
@ -2311,7 +2272,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.3.1;
PRODUCT_BUNDLE_IDENTIFIER = net.johandegraeve.iosxdripreader;
PRODUCT_BUNDLE_IDENTIFIER = net.johandegraeve.beatit;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "xdrip/xdrip-Bridging-Header.h";
@ -2339,7 +2300,7 @@
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.3.1;
PRODUCT_BUNDLE_IDENTIFIER = net.johandegraeve.iosxdripreader;
PRODUCT_BUNDLE_IDENTIFIER = net.johandegraeve.beatit;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "xdrip/xdrip-Bridging-Header.h";
@ -2375,13 +2336,14 @@
F8AC429F21B31F160078C348 /* xdrip.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
F8EBB030239701DA0058B0D4 /* xdrip v6.xcdatamodel */,
F8297F41238C3A6400D74D66 /* xdrip v5.xcdatamodel */,
F889CB9D236D8EEC00A81068 /* xdrip v4.xcdatamodel */,
F898EDF0234A494C00BFB79B /* xdrip v3.xcdatamodel */,
F85C4A93233632EC00D6A86F /* xdrip v2.xcdatamodel */,
F8AC42A021B31F160078C348 /* xdrip.xcdatamodel */,
);
currentVersion = F8297F41238C3A6400D74D66 /* xdrip v5.xcdatamodel */;
currentVersion = F8EBB030239701DA0058B0D4 /* xdrip v6.xcdatamodel */;
path = xdrip.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;

View File

@ -26,75 +26,75 @@ enum ConstantsLog {
static let debuglogging = "xdripdebuglogging"
/// G5
static let categoryCGMG5 = "categoryCGMG5"
static let categoryCGMG5 = "CGMG5"
/// GNSEntry
static let categoryCGMGNSEntry = "categoryCGMGNSEntry"
static let categoryCGMGNSEntry = "CGMGNSEntry"
/// Blucon
static let categoryBlucon = "categoryBlucon"
static let categoryBlucon = "Blucon"
/// core data manager
static let categoryCoreDataManager = "categoryCoreDataManager"
static let categoryCoreDataManager = "CoreDataManager"
/// application data bgreadings
static let categoryApplicationDataBgReadings = "categoryApplicationDataBgReadings"
static let categoryApplicationDataBgReadings = "ApplicationDataBgReadings"
/// application data calibrations
static let categoryApplicationDataCalibrations = "categoryApplicationDataCalibrations"
static let categoryApplicationDataCalibrations = "ApplicationDataCalibrations"
/// application data sensors
static let categoryApplicationDataSensors = "categoryApplicationDataSensors"
static let categoryApplicationDataSensors = "ApplicationDataSensors"
/// application data alerttypes
static let categoryApplicationDataAlertTypes = "categoryApplicationDataAlertTypes"
static let categoryApplicationDataAlertTypes = "ApplicationDataAlertTypes"
/// application data alertentries
static let categoryApplicationDataAlertEntries = "categoryApplicationDataAlertEntries"
static let categoryApplicationDataAlertEntries = "ApplicationDataAlertEntries"
/// application data for M5Stack
static let categoryApplicationDataM5Stacks = "categoryApplicationDataM5Stacks"
static let categoryApplicationDataM5Stacks = "ApplicationDataM5Stacks"
/// application for for M5StackName
static let categoryApplicationDataM5StackNames = "categoryApplicationDataM5StackNames"
static let categoryApplicationDataM5StackNames = "ApplicationDataM5StackNames"
/// nightscout uploader
static let categoryNightScoutUploadManager = "categoryNightScoutUploadManager"
static let categoryNightScoutUploadManager = "NightScoutUploadManager"
/// nightscout follow
static let categoryNightScoutFollowManager = "categoryNightScoutFollowManager"
static let categoryNightScoutFollowManager = "NightScoutFollowManager"
/// alertmanager
static let categoryAlertManager = "categoryAlertManager"
static let categoryAlertManager = "AlertManager"
/// playsound
static let categoryPlaySound = "categoryPlaySound"
static let categoryPlaySound = "PlaySound"
/// healthkit manager
static let categoryHealthKitManager = "categoryHealthKitManager"
static let categoryHealthKitManager = "HealthKitManager"
/// SettingsViewHealthKitSettingsViewModel
static let categorySettingsViewHealthKitSettingsViewModel = "categorySettingsViewHealthKitSettingsViewModel"
static let categorySettingsViewHealthKitSettingsViewModel = "SettingsViewHealthKitSettingsViewModel"
/// dexcom share upload manager
static let categoryDexcomShareUploadManager = "categoryDexcomShareUploadManager"
static let categoryDexcomShareUploadManager = "DexcomShareUploadManager"
/// droplet 1
static let categoryCGMDroplet1 = "categoryCGMDroplet1"
static let categoryCGMDroplet1 = "CGMDroplet1"
/// bluereader
static let categoryCGMBlueReader = "categoryCGMBlueReader"
static let categoryCGMBlueReader = "CGMBlueReader"
/// LibreOOPClient
static let categoryLibreOOPClient = "categoryLibreOOPClient"
static let categoryLibreOOPClient = "LibreOOPClient"
/// for use in M5Stack
static let categoryM5StackBluetoothTransmitter = "categoryM5StackBluetoothTransmitter"
static let categoryM5StackBluetoothTransmitter = "M5StackBluetoothTransmitter"
/// BluetoothPeripheralManager logging
static let categoryBluetoothPeripheralManager = "categoryBluetoothPeripheralManager"
static let categoryBluetoothPeripheralManager = "BluetoothPeripheralManager"
/// StatusChartsManager logging
static let categoryGlucoseChartManager = "categoryGlucoseChartManager"
static let categoryGlucoseChartManager = "GlucoseChartManager"
}

View File

@ -1,50 +0,0 @@
import Foundation
import os
import CoreData
class M5StackNameAccessor {
// MARK: - Properties
/// CoreDataManager to use
private let coreDataManager:CoreDataManager
/// for logging
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryApplicationDataM5StackNames)
// MARK: - initializer
init(coreDataManager:CoreDataManager) {
self.coreDataManager = coreDataManager
}
// MARK: Public functions
func getM5StackNames() -> [M5StackName] {
// create fetchRequest
let fetchRequest: NSFetchRequest<M5StackName> = M5StackName.fetchRequest()
// fetch the M5StackNames
var M5StackNames = [M5StackName]()
coreDataManager.mainManagedObjectContext.performAndWait {
do {
// Execute Fetch Request
M5StackNames = try fetchRequest.execute()
} catch {
let fetchError = error as NSError
trace("in getM5StackNames, Unable to Execute M5StackNames Fetch Request : %{public}@", log: self.log, type: .error, fetchError.localizedDescription)
}
}
return M5StackNames
}
func deleteM5StackName(m5StackName: M5StackName) {
coreDataManager.mainManagedObjectContext.delete(m5StackName)
}
}

View File

@ -4,8 +4,8 @@ import CoreData
/// M5Stack
///
/// M5Stack has
/// - an address (received form M5Stack),
/// - a name (also received from M5Stack),
/// - an address (received from the real device),
/// - a name (also received from the real device),
/// - shouldConnect with default value true, if true then xdrip will automatically try to connect at app launch
/// - blePassword : optional, if this value is set, then it means this M5Stack does have an internally stored password created during M5Stack launch. If it is not set, then the password in the userdefaults will be used. If that is also nil, then the xdrip app can not authenticate towards the M5Stack
/// - textColor : color to use, see M5StackColor
@ -15,13 +15,13 @@ import CoreData
/// - brightness : value between 0 and 100
public class M5Stack: NSManagedObject {
/// this property is not stored in coreData. It is used to keep track of parameter updates sent to the M5Stack. If value is true, then xdrip needs to send an update off all parameters to this M5Stack as soon as possible (ie when connected)
public var parameterUpdateNeeded = true
/// explanation, see function parameterUpdateNotNeededAtNextConnect in protocol BluetoothPeripheral
public var parameterUpdateNeeded: Bool = false
/// create M5Stack, shouldconnect default value = true
/// - parameters:
/// - rotation is internally stored as Int32, actual value should always be between 0 and 360 so UInt16 as parameter is sufficient.
init(address: String, name: String, textColor: M5StackColor, backGroundColor: M5StackColor, rotation: UInt16, brightness: Int, nsManagedObjectContext:NSManagedObjectContext) {
init(address: String, name: String, textColor: M5StackColor, backGroundColor: M5StackColor, rotation: UInt16, brightness: Int, alias: String?, nsManagedObjectContext:NSManagedObjectContext) {
let entity = NSEntityDescription.entity(forEntityName: "M5Stack", in: nsManagedObjectContext)!
@ -34,6 +34,7 @@ public class M5Stack: NSManagedObject {
self.backGroundColor = Int32(backGroundColor.rawValue)
self.rotation = Int32(rotation)
self.brightness = Int16(brightness)
self.alias = alias
}

View File

@ -2,7 +2,7 @@
// M5Stack+CoreDataProperties.swift
//
//
// Created by Johan Degraeve on 25/11/2019.
// Created by Johan Degraeve on 03/12/2019.
//
//
@ -19,11 +19,11 @@ extension M5Stack {
@NSManaged public var address: String
@NSManaged public var backGroundColor: Int32
@NSManaged public var blepassword: String?
@NSManaged public var brightness: Int16
@NSManaged public var name: String
@NSManaged public var rotation: Int32
@NSManaged public var shouldconnect: Bool
@NSManaged public var textcolor: Int32
@NSManaged public var brightness: Int16
@NSManaged public var m5StackName: M5StackName?
@NSManaged public var alias: String?
}

View File

@ -1,31 +0,0 @@
//
// M5StackName+CoreDataClass.swift
//
//
// Created by Johan Degraeve on 22/09/2019.
//
//
import Foundation
import CoreData
public class M5StackName: NSManagedObject {
/// create M5StackName
init(address: String, userDefinedName: String, nsManagedObjectContext:NSManagedObjectContext) {
let entity = NSEntityDescription.entity(forEntityName: "M5StackName", in: nsManagedObjectContext)!
super.init(entity: entity, insertInto: nsManagedObjectContext)
self.address = address
self.userDefinedName = userDefinedName
}
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
}

View File

@ -1,22 +0,0 @@
//
// M5StackName+CoreDataProperties.swift
//
//
// Created by Johan Degraeve on 22/09/2019.
//
//
import Foundation
import CoreData
extension M5StackName {
@nonobjc public class func fetchRequest() -> NSFetchRequest<M5StackName> {
return NSFetchRequest<M5StackName>(entityName: "M5StackName")
}
@NSManaged public var address: String
@NSManaged public var userDefinedName: String
}

View File

@ -3,6 +3,6 @@
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>xdrip v5.xcdatamodel</string>
<string>xdrip v6.xcdatamodel</string>
</dict>
</plist>

View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="15400" systemVersion="19A603" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="AlertEntry" representedClassName=".AlertEntry" syncable="YES">
<attribute name="alertkind" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="start" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="value" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<relationship name="alertType" maxCount="1" deletionRule="No Action" destinationEntity="AlertType" inverseName="alertEntries" inverseEntity="AlertType"/>
</entity>
<entity name="AlertType" representedClassName=".AlertType" syncable="YES">
<attribute name="enabled" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String"/>
<attribute name="overridemute" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="snooze" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="snoozeperiod" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="soundname" optional="YES" attributeType="String"/>
<attribute name="vibrate" attributeType="Boolean" usesScalarValueType="YES"/>
<relationship name="alertEntries" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="AlertEntry" inverseName="alertType" inverseEntity="AlertEntry"/>
</entity>
<entity name="BgReading" representedClassName=".BgReading" syncable="YES">
<attribute name="a" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="ageAdjustedRawValue" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="b" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="c" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="calculatedValue" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="calculatedValueSlope" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="calibrationFlag" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="deviceName" optional="YES" attributeType="String"/>
<attribute name="filteredCalculatedValue" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="filteredData" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="hideSlope" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="id" attributeType="String"/>
<attribute name="ra" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rawData" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rb" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="rc" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="timeStamp" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="calibration" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Calibration" inverseName="bgreadings" inverseEntity="Calibration"/>
<relationship name="sensor" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Sensor" inverseName="readings" inverseEntity="Sensor"/>
</entity>
<entity name="Calibration" representedClassName=".Calibration" syncable="YES">
<attribute name="adjustedRawValue" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="bg" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="deviceName" optional="YES" attributeType="String"/>
<attribute name="distanceFromEstimate" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="estimateRawAtTimeOfCalibration" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="id" attributeType="String"/>
<attribute name="intercept" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="possibleBad" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="rawTimeStamp" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="rawValue" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="sensorConfidence" attributeType="Double" usesScalarValueType="YES"/>
<attribute name="slope" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="slopeConfidence" attributeType="Double" defaultValueString="0.0" usesScalarValueType="YES"/>
<attribute name="timeStamp" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="bgreadings" toMany="YES" deletionRule="Deny" destinationEntity="BgReading" inverseName="calibration" inverseEntity="BgReading"/>
<relationship name="sensor" maxCount="1" deletionRule="Nullify" destinationEntity="Sensor" inverseName="calibrations" inverseEntity="Sensor"/>
</entity>
<entity name="M5Stack" representedClassName=".M5Stack" syncable="YES">
<attribute name="address" attributeType="String"/>
<attribute name="alias" optional="YES" attributeType="String"/>
<attribute name="backGroundColor" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="blepassword" optional="YES" attributeType="String"/>
<attribute name="brightness" optional="YES" attributeType="Integer 16" defaultValueString="100" usesScalarValueType="YES"/>
<attribute name="name" attributeType="String"/>
<attribute name="rotation" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="shouldconnect" attributeType="Boolean" usesScalarValueType="YES"/>
<attribute name="textcolor" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES"/>
</entity>
<entity name="Sensor" representedClassName=".Sensor" syncable="YES">
<attribute name="endDate" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="id" attributeType="String"/>
<attribute name="startDate" attributeType="Date" usesScalarValueType="NO"/>
<relationship name="calibrations" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="Calibration" inverseName="sensor" inverseEntity="Calibration"/>
<relationship name="readings" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="BgReading" inverseName="sensor" inverseEntity="BgReading"/>
</entity>
<elements>
<element name="AlertEntry" positionX="-648" positionY="189" width="128" height="105"/>
<element name="AlertType" positionX="-657" positionY="180" width="128" height="165"/>
<element name="BgReading" positionX="-285.87109375" positionY="31.9921875" width="128" height="330"/>
<element name="Calibration" positionX="-859.21484375" positionY="46.21484375" width="128" height="285"/>
<element name="M5Stack" positionX="-657" positionY="180" width="128" height="178"/>
<element name="Sensor" positionX="-603.0859375" positionY="482.2890625" width="128" height="120"/>
</elements>
</model>

View File

@ -0,0 +1,775 @@
import Foundation
import os
import CoreBluetooth
import CoreData
class BluetoothPeripheralManager: NSObject {
// MARK: - private properties
/// CoreDataManager to use
private let coreDataManager:CoreDataManager
/// for logging
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryBluetoothPeripheralManager)
/// dictionary with key = an instance of BluetoothPeripheral, and value an instance of BluetoothTransmitter. Value can be nil in which case we found a BluetoothPeripheral in the coredata but shouldconnect == false so we don't instanstiate a BluetoothTransmitter
//private var m5StacksBlueToothTransmitters = [BluetoothPeripheral : BluetoothTransmitter?]()
/// all currently known BluetoothPeripheral's (MStacks, cgmtransmitters, watlaa , ...)
private var bluetoothPeripherals: [BluetoothPeripheral] = []
/// the bluetoothTransmitter's, array must have the same size as bluetoothPeripherals. For each element in bluetoothPeripherals, there's an element at the same index in bluetoothTransmitters, which may be nil. nil value means user selected not to connect
private var bluetoothTransmitters: [BluetoothTransmitter?] = []
/// to access m5Stack entity in coredata
private var m5StackAccessor: M5StackAccessor
/// reference to BgReadingsAccessor
private var bgReadingsAccessor: BgReadingsAccessor
/// if scan is called, and a connection is successfully made to a new device, then a new M5Stack must be created, and this function will be called. It is owned by the UIViewController that calls the scan function
private var callBackAfterDiscoveringDevice: ((BluetoothPeripheral) -> Void)?
/// if scan is called, an instance of M5StackBluetoothTransmitter is created with address and name. The new instance will be assigned to this variable, temporary, until a connection is made
private var tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral: BluetoothTransmitter?
/// to solve problem that sometemes UserDefaults key value changes is triggered twice for just one change
private let keyValueObserverTimeKeeper:KeyValueObserverTimeKeeper = KeyValueObserverTimeKeeper()
// MARK: - initializer
init(coreDataManager: CoreDataManager) {
// initialize properties
self.coreDataManager = coreDataManager
self.m5StackAccessor = M5StackAccessor(coreDataManager: coreDataManager)
self.bgReadingsAccessor = BgReadingsAccessor(coreDataManager: coreDataManager)
super.init()
// initialize m5Stacks
let m5Stacks = m5StackAccessor.getM5Stacks()
for m5Stack in m5Stacks {
// add it to the list of bluetoothPeripherals
bluetoothPeripherals.append(m5Stack)
if m5Stack.shouldconnect {
// create an instance of M5StackBluetoothTransmitter, M5StackBluetoothTransmitter will automatically try to connect to the M5Stack with the address that is stored in m5Stack
// add it to the array of bluetoothTransmitters
bluetoothTransmitters.append(M5StackBluetoothTransmitter(address: m5Stack.address, name: m5Stack.name, delegate: self, blePassword: m5Stack.blepassword))
} else {
// shouldn't connect, so don't create an instance of M5StackBluetoothTransmitter
// but append a nil element
bluetoothTransmitters.append(nil)
}
// each time the app launches, we will send the parameters to all BluetoothPeripherals
m5Stack.parameterUpdateNeededAtNextConnect()
}
// when user changes M5Stack related settings, then the transmitter need to get that info
addObservers()
}
// MARK: - public functions
/// will send latest reading to all BluetoothTransmitters that need this info and only if it's less than 5 minutes old
/// - parameters:
/// - to :if nil then latest reading will be sent to all connected BluetoothTransmitters that need this info, otherwise only to the specified BluetoothTransmitter
///
/// this function has knowledge about different types of BluetoothTransmitter and knows to which it should send to reading, to which not
public func sendLatestReading(to bluetoothPeripheral: BluetoothPeripheral? = nil) {
// get reading of latest 5 minutes
let bgReadingToSend = bgReadingsAccessor.getLatestBgReadings(limit: 1, fromDate: Date(timeIntervalSinceNow: -5 * 60), forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// check that there's at least 1 reading available
guard bgReadingToSend.count >= 1 else {
trace("in sendLatestReading, there's no recent reading to send", log: self.log, type: .info)
return
}
if let bluetoothPeripheral = bluetoothPeripheral {
guard let index = firstIndexInBluetoothPeripherals(bluetoothPeripheral: bluetoothPeripheral), let bluetoothTransmitter = bluetoothTransmitters[index] else {return}
// get type of bluetoothPeripheral
let bluetoothPeripheralType = bluetoothPeripheral.bluetoothPeripheralType()
// using bluetoothPeripheralType here so that whenever bluetoothPeripheralType is extended with new cases, we don't forget to handle them
switch bluetoothPeripheralType {
case .M5Stack:
if let m5StackBluetoothTransmitter = bluetoothTransmitter as? M5StackBluetoothTransmitter {
_ = m5StackBluetoothTransmitter.writeBgReadingInfo(bgReading: bgReadingToSend[0])
}
}
} else {
// send the reading to all bluetoothPeripherals that want to receive a reading
// to make sure new types are not forgotten, I use switch
for bluetoothPeripheralType in BluetoothPeripheralType.allCases {
switch bluetoothPeripheralType {
case .M5Stack:
for bluetoothPeripheral in bluetoothPeripherals {
// get the bluetooth transmitter, and if it's an M5StackBluetoothTransmitter, then send the bgReading
if let bluetoothTransmitter = getBluetoothTransmitter(for: bluetoothPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = bluetoothTransmitter as? M5StackBluetoothTransmitter {
_ = m5StackBluetoothTransmitter.writeBgReadingInfo(bgReading: bgReadingToSend[0])
}
}
}
}
}
}
// MARK: - private functions
/// when user changes M5Stack related settings, then the transmitter need to get that info, add observers
private func addObservers() {
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiName1.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiName2.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiName3.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiPassword1.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiPassword2.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiPassword3.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackBlePassword.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.bloodGlucoseUnitIsMgDl.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutUrl.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutAPIKey.rawValue, options: .new, context: nil)
}
/// disconnect from bluetoothPeripheral - and don't reconnect - set shouldconnect to false
private func disconnect(fromBluetoothPeripheral bluetoothPeripheral: BluetoothPeripheral) {
// device should not reconnect after disconnecting
bluetoothPeripheral.dontTryToConnectToThisBluetoothPeripheral()
// save in coredata
coreDataManager.saveChanges()
if let bluetoothTransmitter = getBluetoothTransmitter(for: bluetoothPeripheral, createANewOneIfNecesssary: false) {
bluetoothTransmitter.disconnect(reconnectAfterDisconnect: false)
}
}
private func firstIndexInBluetoothPeripherals(bluetoothPeripheral: BluetoothPeripheral) -> Int? {
return bluetoothPeripherals.firstIndex(where: {$0.getAddress() == bluetoothPeripheral.getAddress()})
}
private func createNewTransmitter(type: BluetoothPeripheralType) -> BluetoothTransmitter {
switch type {
case .M5Stack:
return M5StackBluetoothTransmitter(address: nil, name: nil, delegate: self, blePassword: UserDefaults.standard.m5StackBlePassword)
}
}
private func getTransmitterType(for bluetoothTransmitter:BluetoothTransmitter) -> BluetoothPeripheralType {
for bluetoothPeripheralType in BluetoothPeripheralType.allCases {
// using switch through all cases, to make sure that new future types are supported
switch bluetoothPeripheralType {
case .M5Stack:
if bluetoothTransmitter is M5StackBluetoothTransmitter {
return .M5Stack
}
}
}
// normally we shouldn't get here, but we need to return a value
fatalError("BluetoothPeripheralManager : getTransmitterType did not find a valid type")
}
// MARK:- override observe function
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
guard let keyPath = keyPath else {return}
guard let keyPathEnum = UserDefaults.Key(rawValue: keyPath) else {return}
// first check keyValueObserverTimeKeeper
switch keyPathEnum {
case UserDefaults.Key.m5StackWiFiName1, UserDefaults.Key.m5StackWiFiName2, UserDefaults.Key.m5StackWiFiName3, UserDefaults.Key.m5StackWiFiPassword1, UserDefaults.Key.m5StackWiFiPassword2, UserDefaults.Key.m5StackWiFiPassword3, UserDefaults.Key.nightScoutAPIKey, UserDefaults.Key.nightScoutUrl, UserDefaults.Key.bloodGlucoseUnitIsMgDl :
// transmittertype change triggered by user, should not be done within 200 ms
if !keyValueObserverTimeKeeper.verifyKey(forKey: keyPathEnum.rawValue, withMinimumDelayMilliSeconds: 200) {
return
}
default:
break
}
// loop through all bluetoothPeripheralTypes. Do a check on bluetoothPeripheralType, depending on type, loop through all bluetoothPeripherals, and if correct type, write the value to the peripheral
// this usage of bluetoothPeripheralType is done to make sure this code is not forgotten when adding a bluetoothPeripheralType
for bluetoothPeripheralType in BluetoothPeripheralType.allCases {
switch bluetoothPeripheralType {
case .M5Stack:
for bluetoothPeripheral in bluetoothPeripherals {
// get the bluetooth transmitter, and if it's an M5StackBluetoothTransmitter, then send the bgReading
if let bluetoothTransmitter = getBluetoothTransmitter(for: bluetoothPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = bluetoothTransmitter as? M5StackBluetoothTransmitter {
// check that bluetoothPeripheral is of type M5Stack, if not then this might be a coding error
guard let m5Stack = bluetoothPeripheral as? M5Stack else {
trace("in observeValue, transmitter is of type M5StackBluetoothTransmitter but peripheral is not M5Stack", log: self.log, type: .error)
return
}
// is value successfully written or not
var success = false
switch keyPathEnum {
case UserDefaults.Key.m5StackWiFiName1:
success = m5StackBluetoothTransmitter.writeWifiName(name: UserDefaults.standard.m5StackWiFiName1, number: 1)
case UserDefaults.Key.m5StackWiFiName2:
success = m5StackBluetoothTransmitter.writeWifiName(name: UserDefaults.standard.m5StackWiFiName2, number: 2)
case UserDefaults.Key.m5StackWiFiName3:
success = m5StackBluetoothTransmitter.writeWifiName(name: UserDefaults.standard.m5StackWiFiName3, number: 3)
case UserDefaults.Key.m5StackWiFiPassword1:
success = m5StackBluetoothTransmitter.writeWifiPassword(password: UserDefaults.standard.m5StackWiFiPassword1, number: 1)
case UserDefaults.Key.m5StackWiFiPassword2:
success = m5StackBluetoothTransmitter.writeWifiPassword(password: UserDefaults.standard.m5StackWiFiPassword2, number: 2)
case UserDefaults.Key.m5StackWiFiPassword3:
success = m5StackBluetoothTransmitter.writeWifiPassword(password: UserDefaults.standard.m5StackWiFiPassword3, number: 3)
case UserDefaults.Key.m5StackBlePassword:
// only if the password in the settings is not nil, and if the m5Stack doesn't have a password yet, then we will store it in the M5Stack.
if let blePassword = UserDefaults.standard.m5StackBlePassword, m5Stack.blepassword == nil {
m5Stack.blepassword = blePassword
}
case UserDefaults.Key.bloodGlucoseUnitIsMgDl:
success = m5StackBluetoothTransmitter.writeBloodGlucoseUnit(isMgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl)
case UserDefaults.Key.nightScoutAPIKey:
success = m5StackBluetoothTransmitter.writeNightScoutAPIKey(apiKey: UserDefaults.standard.nightScoutAPIKey)
case UserDefaults.Key.nightScoutUrl:
success = m5StackBluetoothTransmitter.writeNightScoutUrl(url: UserDefaults.standard.nightScoutUrl)
default:
break
}
// if not successful then set needs parameter update to true for the m5Stack
if !success {
bluetoothPeripheral.parameterUpdateNeededAtNextConnect()
}
} else {
// seems to be an M5Stack which is currently disconnected - need to set parameterUpdateNeeded = true, so that all parameters will be sent as soon as reconnect occurs
bluetoothPeripheral.parameterUpdateNeededAtNextConnect()
}
}
}
}
}
}
// MARK: - extensions
// MARK: extension BluetoothPeripheralManaging
extension BluetoothPeripheralManager: BluetoothPeripheralManaging {
/// to scan for a new BluetoothPeripheral - callback will be called when a new BluetoothPeripheral is found and connected
func startScanningForNewDevice(type: BluetoothPeripheralType, callback: @escaping (BluetoothPeripheral) -> Void) {
callBackAfterDiscoveringDevice = callback
tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = createNewTransmitter(type: type)
_ = tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral?.startScanning()
}
/// stops scanning for new device
func stopScanningForNewDevice() {
if let tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral {
tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral.stopScanning()
self.tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = nil
}
}
func isScanning() -> Bool {
if let tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral {
return tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral.isScanning()
} else {
return false
}
}
/// try to connect to the M5Stack
func connect(to bluetoothPeripheral: BluetoothPeripheral) {
// the trick : by calling bluetoothTransmitter(forBluetoothPeripheral: bluetoothPeripheral, createANewOneIfNecesssary: true), there's two cases
// - either the bluetoothTransmitter already exists but not connected, it will be found in the call to bluetoothTransmitter and returned, then we connect to it
// - either the bluetoothTransmitter doesn't exist yet. It will be created. We assum here that bluetoothPeripheral has a mac address, as a consequence the BluetoothTransmitter will automatically try to connect. Here we try to connect again, but that's no issue
let transmitter = getBluetoothTransmitter(for: bluetoothPeripheral, createANewOneIfNecesssary: true)
transmitter?.connect()
}
/// returns the BluetoothPeripheral for the specified BluetoothTransmitter
/// - parameters:
/// - for : the bluetoothTransmitter, for which BluetoothPeripheral should be returned
func getBluetoothPeripheral(for bluetoothTransmitter: BluetoothTransmitter) -> BluetoothPeripheral {
guard let index = bluetoothTransmitters.firstIndex(of: bluetoothTransmitter) else {
fatalError("in BluetoothPeripheralManager, function getBluetoothPeripherals, could not find specified bluetoothTransmitter")
}
return bluetoothPeripherals[index]
}
/// returns the bluetoothTransmitter for the bluetoothPeripheral
/// - parameters:
/// - forBluetoothPeripheral : the bluetoothPeripheral for which bluetoothTransmitter should be returned
/// - createANewOneIfNecesssary : if bluetoothTransmitter is nil, then should one be created ?
func getBluetoothTransmitter(for bluetoothPeripheral: BluetoothPeripheral, createANewOneIfNecesssary: Bool) -> BluetoothTransmitter? {
if let index = firstIndexInBluetoothPeripherals(bluetoothPeripheral: bluetoothPeripheral) {
if let bluetoothTransmitter = bluetoothTransmitters[index] {
return bluetoothTransmitter
}
if createANewOneIfNecesssary {
var newTransmitter: BluetoothTransmitter? = nil
switch bluetoothPeripheral.bluetoothPeripheralType() {
case .M5Stack:
if let m5Stack = bluetoothPeripheral as? M5Stack {
newTransmitter = M5StackBluetoothTransmitter(address: m5Stack.address, name: m5Stack.name, delegate: self, blePassword: UserDefaults.standard.m5StackBlePassword)
}
}
bluetoothTransmitters[index] = newTransmitter
return newTransmitter
}
}
return nil
}
/// deletes the BluetoothPeripheral in coredata, and also the corresponding BluetoothTransmitter if there is one will be deleted
func deleteBluetoothPeripheral(bluetoothPeripheral: BluetoothPeripheral) {
// find the bluetoothPeripheral in array bluetoothPeripherals, if it's not there then this looks like a coding error
guard let index = firstIndexInBluetoothPeripherals(bluetoothPeripheral: bluetoothPeripheral) else {
trace("in deleteBluetoothPeripheral but bluetoothPeripheral not found in bluetoothPeripherals, looks like a coding error ", log: self.log, type: .error)
return
}
// set bluetoothTransmitter to nil, this will also initiate a disconnect
bluetoothTransmitters[index] = nil
// delete in coredataManager
coreDataManager.mainManagedObjectContext.delete(bluetoothPeripherals[index] as! NSManagedObject)
// remove bluetoothTransmitter and bluetoothPeripheral entry from the two arrays
bluetoothTransmitters.remove(at: index)
bluetoothPeripherals.remove(at: index)
// save in coredataManager
coreDataManager.saveChanges()
}
/// - returns: the bluetoothPeripheral's managed by this BluetoothPeripheralManager
func getBluetoothPeripherals() -> [BluetoothPeripheral] {
return bluetoothPeripherals
}
/// - returns: the bluetoothTransmitters managed by this BluetoothPeripheralManager
func getBluetoothTransmitters() -> [BluetoothTransmitter] {
var bluetoothTransmitters: [BluetoothTransmitter] = []
for bluetoothTransmitter in self.bluetoothTransmitters {
if let bluetoothTransmitter = bluetoothTransmitter {
bluetoothTransmitters.append(bluetoothTransmitter)
}
}
return bluetoothTransmitters
}
/// bluetoothtransmitter for this bluetoothPeripheral will be deleted, as a result this will also disconnect the bluetoothPeripheral
func setBluetoothTransmitterToNil(forBluetoothPeripheral bluetoothPeripheral: BluetoothPeripheral) {
if let index = firstIndexInBluetoothPeripherals(bluetoothPeripheral: bluetoothPeripheral) {
bluetoothTransmitters[index] = nil
}
}
}
// MARK: extension M5StackBluetoothDelegate
extension BluetoothPeripheralManager: BluetoothTransmitterDelegate {
func didConnectTo(bluetoothTransmitter: BluetoothTransmitter) {
// if tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral is nil, then this is a connection to an already known/stored BluetoothTransmitter. BluetoothPeripheralManager is not interested in this info.
guard let tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral else {
trace("in didConnect, tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral is nil, no further processing", log: self.log, type: .info)
return
}
// check that address and name are not nil, otherwise this looks like a codding error
guard let deviceAddressNewTransmitter = bluetoothTransmitter.deviceAddress, let deviceNameNewTransmitter = bluetoothTransmitter.deviceName else {
trace("in didConnect, address or name of new transmitter is nil, looks like a coding error", log: self.log, type: .error)
return
}
// check that tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral and the bluetoothTransmitter to which connection is made are actually the same objects, otherwise it's a connection that is made to a already known/stored BluetoothTransmitter
guard tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral == bluetoothTransmitter else {
trace("in didConnect, tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral is not nil and not equal to bluetoothTransmitter", log: self.log, type: .info)
return
}
// check that it's a peripheral for which we don't know yet the address
for buetoothPeripheral in bluetoothPeripherals {
if buetoothPeripheral.getAddress() == deviceAddressNewTransmitter {
trace("in didConnect, transmitter address already known. This is not a new device, will disconnect", log: self.log, type: .info)
// it's an already known BluetoothTransmitter, not storing this, on the contrary disconnecting because maybe it's a bluetoothTransmitter already known for which user has preferred not to connect to
// If we're actually waiting for a new scan result, then there's an instance of BluetoothTransmitter stored in tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral - but this one stopped scanning, so let's recreate an instance of BluetoothTransmitter
self.tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = createNewTransmitter(type: getTransmitterType(for: bluetoothTransmitter))
_ = self.tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral?.startScanning()
return
}
}
// it's a new peripheral that we will store. No need to continue scanning
bluetoothTransmitter.stopScanning()
// initialize new bluetoothPeripheral
var newBluetoothPeripheral: BluetoothPeripheral?
for bluetoothPeripheralType in BluetoothPeripheralType.allCases {
switch bluetoothPeripheralType {
case .M5Stack:
// create a new BluetoothPeripheral of type M5Stack
let newM5Stack = M5Stack(address: deviceAddressNewTransmitter, name: deviceNameNewTransmitter, textColor: UserDefaults.standard.m5StackTextColor ?? ConstantsM5Stack.defaultTextColor, backGroundColor: ConstantsM5Stack.defaultBackGroundColor, rotation: ConstantsM5Stack.defaultRotation, brightness: 100, alias: nil, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// assign password stored in UserDefaults (might be nil)
newM5Stack.blepassword = UserDefaults.standard.m5StackBlePassword
// add to list of bluetoothPeripherals and bluetoothTransmitters
bluetoothPeripherals.append(newM5Stack)
bluetoothTransmitters.append(bluetoothTransmitter)
// no need to keep a reference to the bluetothTransmitter, this is now stored in m5StacksBlueToothTransmitters
self.tempBlueToothTransmitterWhileScanningForNewBluetoothPeripheral = nil
// assign newM5stack to newBluetoothPeripheral
newBluetoothPeripheral = newM5Stack
}
}
// call the callback function
if let callBackAfterDiscoveringDevice = callBackAfterDiscoveringDevice, let newBluetoothPeripheral = newBluetoothPeripheral {
callBackAfterDiscoveringDevice(newBluetoothPeripheral)
self.callBackAfterDiscoveringDevice = nil
}
}
func deviceDidUpdateBluetoothState(state: CBManagerState, bluetoothTransmitter: BluetoothTransmitter) {
trace("in deviceDidUpdateBluetoothState, no further action", log: self.log, type: .info)
}
func error(message: String) {
trace("in error, no further action", log: self.log, type: .info)
}
func didDisconnectFrom(bluetoothTransmitter: BluetoothTransmitter) {
// no further action, This is for UIViewcontroller's that also receive this info, means info can only be shown if this happens while user has one of the UIViewcontrollers open
trace("in didDisconnectFrom", log: self.log, type: .info)
}
}
// MARK: conform to M5StackBluetoothTransmitterDelegate
extension BluetoothPeripheralManager: M5StackBluetoothTransmitterDelegate {
/// did the app successfully authenticate towards M5Stack, if no, then disconnect will be done
func authentication(success: Bool, m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
trace("in authentication with success = %{public}@", log: self.log, type: .info, success.description)
// if authentication not successful then disconnect and don't reconnect, user should verify password or reset the M5Stack, disconnect and set shouldconnect to false, permenantly (ie store in core data)
// disconnection is done because maybe another device is trying to connect to the M5Stack, need to make it free
// also set shouldConnect to false (note that this is also done in M5StackViewController if an instance of that exists, no issue, shouldConnect will be set to false two times
if !success {
// should find the m5StackBluetoothTransmitter in bluetoothTransmitters and it should be an M5Stack
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter), let m5Stack = bluetoothPeripherals[index] as? M5Stack else {return}
// don't try to reconnect after disconnecting
m5Stack.shouldconnect = false
// store in core data
coreDataManager.saveChanges()
// disconnect
disconnect(fromBluetoothPeripheral: m5Stack)
}
}
/// there's no ble password set, user should set it in the settings - disconnect will be called, shouldconnect is set to false
func blePasswordMissing(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
trace("in blePasswordMissing", log: self.log, type: .info)
// should find the m5StackBluetoothTransmitter in bluetoothTransmitters and it should be an M5Stack
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter), let m5Stack = bluetoothPeripherals[index] as? M5Stack else {return}
// don't try to reconnect after disconnecting
m5Stack.shouldconnect = false
// store in core data
coreDataManager.saveChanges()
// disconnect
disconnect(fromBluetoothPeripheral: m5Stack)
}
/// if a new ble password is received from M5Stack
func newBlePassWord(newBlePassword: String, m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
trace("in newBlePassWord, storing the password in M5Stack", log: self.log, type: .info)
// should find the m5StackBluetoothTransmitter in bluetoothTransmitters and it should be an M5Stack
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter), let m5Stack = bluetoothPeripherals[index] as? M5Stack else {return}
// possibily this is a new scanned m5stack, calling coreDataManager.saveChanges() but still the user may be in M5stackviewcontroller and decide not to save the m5stack, bad luck
m5Stack.blepassword = newBlePassword
coreDataManager.saveChanges()
}
/// it's an M5Stack without password configured in the ini file. xdrip app has been requesting temp password to M5Stack but this was already done once. M5Stack needs to be reset. - disconnect will be called, shouldconnect is set to false
func m5StackResetRequired(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
trace("in m5StackResetRequired", log: self.log, type: .info)
// should find the m5StackBluetoothTransmitter in bluetoothTransmitters and it should be an M5Stack
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter), let m5Stack = bluetoothPeripherals[index] as? M5Stack else {return}
m5Stack.dontTryToConnectToThisBluetoothPeripheral()
coreDataManager.saveChanges()
// disconnect
disconnect(fromBluetoothPeripheral: m5Stack)
}
/// bluetoothPeripheral is asking for an update of all parameters, send them
func isAskingForAllParameters(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter) else {
trace("in isAskingForAllParameters, could not find index of bluetoothTransmitter, looks like a coding error", log: self.log, type: .error)
return
}
// send all parameters, if successful,then for this m5Stack we can set parameterUpdateNeeded to false
if sendAllParametersToM5Stack(to: m5StackBluetoothTransmitter) {
(bluetoothPeripherals[index] as? M5Stack)?.parameterUpdateNotNeededAtNextConnect()
} else {
// failed, so we need to set parameterUpdateNeeded to true, so that next time it connects we will send all parameters
(bluetoothPeripherals[index] as? M5Stack)?.parameterUpdateNeededAtNextConnect()
}
}
/// will be called if M5Stack is connected, and authentication was successful, BluetoothPeripheralManager can start sending data like parameter updates or bgreadings
func isReadyToReceiveData(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
// should find the m5StackBluetoothTransmitter in bluetoothTransmitters and it should be an M5Stack
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter), let m5Stack = bluetoothPeripherals[index] as? M5Stack else {return}
// if the M5Stack needs new parameters, then send them
if m5Stack.isParameterUpdateNeededAtNextConnect() {
// send all parameters
if sendAllParametersToM5Stack(to: m5StackBluetoothTransmitter) {
m5Stack.parameterUpdateNotNeededAtNextConnect()
}
}
// send latest reading
sendLatestReading(to: m5Stack)
}
// MARK: private functions related to M5Stack
/// send all parameters to m5Stack
/// - parameters:
/// - to : m5StackBluetoothTransmitter to send all parameters
/// - returns:
/// successfully written all parameters or not
private func sendAllParametersToM5Stack(to m5StackBluetoothTransmitter : M5StackBluetoothTransmitter) -> Bool {
// should find the m5StackBluetoothTransmitter in bluetoothTransmitters and it should be an M5Stack
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter), let m5Stack = bluetoothPeripherals[index] as? M5Stack else {return false}
// M5Stack must be ready to receive data
guard m5StackBluetoothTransmitter.isReadyToReceiveData else {
trace("in sendAllParameters, bluetoothTransmitter is not ready to receive data", log: self.log, type: .info)
return false
}
// initialise returnValue, result
var success = true
// send bloodglucoseunit
if !m5StackBluetoothTransmitter.writeBloodGlucoseUnit(isMgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl) {success = false}
// send textColor
if !m5StackBluetoothTransmitter.writeTextColor(textColor: M5StackColor(forUInt16: UInt16(m5Stack.textcolor)) ?? UserDefaults.standard.m5StackTextColor ?? ConstantsM5Stack.defaultTextColor) {success = false}
// send backGroundColor
if !m5StackBluetoothTransmitter.writeBackGroundColor(backGroundColor: M5StackColor(forUInt16: UInt16(m5Stack.backGroundColor)) ?? ConstantsM5Stack.defaultBackGroundColor ) {success = false}
// send rotation
if !m5StackBluetoothTransmitter.writeRotation(rotation: Int(m5Stack.rotation)) {success = false}
// send WiFiSSID's
if let wifiName = UserDefaults.standard.m5StackWiFiName1 {
if !m5StackBluetoothTransmitter.writeWifiName(name: wifiName, number: 1) {success = false}
}
if let wifiName = UserDefaults.standard.m5StackWiFiName2 {
if !m5StackBluetoothTransmitter.writeWifiName(name: wifiName, number: 2) {success = false}
}
if let wifiName = UserDefaults.standard.m5StackWiFiName3 {
if !m5StackBluetoothTransmitter.writeWifiName(name: wifiName, number: 3) {success = false}
}
// send WiFiPasswords
if let wifiPassword = UserDefaults.standard.m5StackWiFiPassword1 {
if !m5StackBluetoothTransmitter.writeWifiPassword(password: wifiPassword, number: 1) {success = false}
}
if let wifiPassword = UserDefaults.standard.m5StackWiFiPassword2 {
if !m5StackBluetoothTransmitter.writeWifiPassword(password: wifiPassword, number: 2) {success = false}
}
if let wifiPassword = UserDefaults.standard.m5StackWiFiPassword3 {
if !m5StackBluetoothTransmitter.writeWifiPassword(password: wifiPassword, number: 3) {success = false}
}
// send nightscout url
if let url = UserDefaults.standard.nightScoutUrl {
if !m5StackBluetoothTransmitter.writeNightScoutUrl(url: url) {success = false}
}
// send nightscout token
if let token = UserDefaults.standard.nightScoutAPIKey {
if !m5StackBluetoothTransmitter.writeNightScoutAPIKey(apiKey: token) {success = false}
}
// return success
return success
}
}

View File

@ -1,33 +1,41 @@
import Foundation
/// used by M5Stack UI view controllers - it's the glue between BluetoothPeripheralManager and UIViewControllers - defines functions to scan for devices, connect/disconnect, delete an M5 stack, change the username, etc.
/// used by BluetoothPeripheral UI view controllers - it's the glue between BluetoothPeripheralManager and UIViewControllers - defines functions to scan for devices, connect/disconnect, delete a BluetoothPeripheral, change the username, etc.
protocol BluetoothPeripheralManaging: AnyObject {
/// to scan for a new M5SStack - callback will be called when a new M5Stack is found and connected
func startScanningForNewDevice(callback: @escaping (M5Stack) -> Void)
/// to scan for a new BluetoothPeripheral - callback will be called when a new BluetoothPeripheral is found and connected
func startScanningForNewDevice(type: BluetoothPeripheralType, callback: @escaping (BluetoothPeripheral) -> Void)
/// will stop scanning, this is again for the case where scanning for a new M5Stack has started
/// will stop scanning, this is again for the case where scanning for a new BluetoothPeripheral has started
func stopScanningForNewDevice()
/// try to connect to the M5Stack
func connect(toM5Stack m5Stack: M5Stack)
/// to know if bluetoothperipheralmanager is currently scanning for a new device
func isScanning() -> Bool
/// returns the M5StackBluetoothTransmitter for the m5stack
/// try to connect to the BluetoothPeripheral
func connect(to bluetoothPeripheral: BluetoothPeripheral)
/// returns the BluetoothTransmitter for the specified bluetoothPeripheral
/// - parameters:
/// - forM5Stack : the m5Stack for which bluetoothTransmitter should be returned
/// - createANewOneIfNecesssary : if bluetoothTransmitter is nil, then should one be created ?
func m5StackBluetoothTransmitter(forM5stack m5Stack: M5Stack, createANewOneIfNecesssary: Bool) -> M5StackBluetoothTransmitter?
/// - for : the bluetoothPeripheral, for which bluetoothTransmitter should be returned
/// - createANewOneIfNecesssary : if there's no instance yet, then should one be created ?
func getBluetoothTransmitter(for bluetoothPeripheral: BluetoothPeripheral, createANewOneIfNecesssary: Bool) -> BluetoothTransmitter?
/// deletes the M5Stack in coredata, and also the corresponding M5StackBluetoothTransmitter if there is one will be deleted
func deleteM5Stack(m5Stack: M5Stack)
/// returns the BluetoothPeripheral for the specified BluetoothTransmitter
/// - parameters:
/// - for : the bluetoothTransmitter, for which BluetoothPeripheral should be returned
func getBluetoothPeripheral(for bluetoothTransmitter: BluetoothTransmitter) -> BluetoothPeripheral
/// - returns: the M5Stack's managed by this BluetoothPeripheralManager
func m5Stacks() -> [M5Stack]
/// deletes the BluetoothPeripheral in coredata, and also the corresponding BluetoothTransmitter if there is one will be deleted
func deleteBluetoothPeripheral(bluetoothPeripheral: BluetoothPeripheral)
/// sets flag m5StacksParameterUpdateNeeded for m5Stack to true
func updateNeeded(forM5Stack m5Stack: M5Stack)
/// - returns: the BluetoothPeripheral's managed by this BluetoothPeripheralManager
func getBluetoothPeripherals() -> [BluetoothPeripheral]
/// bluetoothtransmitter for this m5Stack will be deleted, as a result this will also disconnect the M5Stack
func setBluetoothTransmitterToNil(forM5Stack m5Stack: M5Stack)
/// - returns: the BluetoothTransmittersl's managed by this BluetoothPeripheralManager
func getBluetoothTransmitters() -> [BluetoothTransmitter]
/// bluetoothtransmitter for this bluetoothperiheral will be deleted, as a result this will also disconnect the bluetoothtransmitter
func setBluetoothTransmitterToNil(forBluetoothPeripheral bluetoothPeripheral: BluetoothPeripheral)
}

View File

@ -1,555 +0,0 @@
import Foundation
import os
import CoreBluetooth
class BluetoothPeripheralManager: NSObject {
// MARK: - private properties
/// CoreDataManager to use
private let coreDataManager:CoreDataManager
/// for logging
private var log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryBluetoothPeripheralManager)
/// dictionary with key = an instance of M5Stack, and value an instance of M5StackBluetoothTransmitter. Value can be nil in which case we found an M5Stack in the coredata but shouldconnect == false so we don't instanstiate an M5StackBluetoothTransmitter
private var m5StacksBlueToothTransmitters = [M5Stack : M5StackBluetoothTransmitter?]()
/// to access m5Stack entity in coredata
private var m5StackAccessor: M5StackAccessor
/// reference to BgReadingsAccessor
private var bgReadingsAccessor: BgReadingsAccessor
/// if scan is called, and a connection is successfully made to a new device, then a new M5Stack must be created, and this function will be called. It is owned by the UIViewController that calls the scan function
private var callBackAfterDiscoveringDevice: ((M5Stack) -> Void)?
/// if scan is called, an instance of M5StackBluetoothTransmitter is created with address and name. The new instance will be assigned to this variable, temporary, until a connection is made
private var tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack: M5StackBluetoothTransmitter?
/// to solve problem that sometemes UserDefaults key value changes is triggered twice for just one change
private let keyValueObserverTimeKeeper:KeyValueObserverTimeKeeper = KeyValueObserverTimeKeeper()
// MARK: - initializer
init(coreDataManager: CoreDataManager) {
// initialize properties
self.coreDataManager = coreDataManager
self.m5StackAccessor = M5StackAccessor(coreDataManager: coreDataManager)
self.bgReadingsAccessor = BgReadingsAccessor(coreDataManager: coreDataManager)
super.init()
// initialize m5Stacks
let m5Stacks = m5StackAccessor.getM5Stacks()
for m5Stack in m5Stacks {
if m5Stack.shouldconnect {
// create an instance of M5StackBluetoothTransmitter, M5StackBluetoothTransmitter will automatically try to connect to the M5Stack with the address that is stored in m5Stack
self.m5StacksBlueToothTransmitters[m5Stack] = M5StackBluetoothTransmitter(m5Stack: m5Stack, delegateFixed: self, blePassword: UserDefaults.standard.m5StackBlePassword)
} else {
// shouldn't connect, so don't create an instance of M5StackBluetoothTransmitter
self.m5StacksBlueToothTransmitters[m5Stack] = (M5StackBluetoothTransmitter?).none
}
// each time the app launches, we will send the parameter to all M5Stacks
m5Stack.parameterUpdateNeeded = true
}
// when user changes M5Stack related settings, then the transmitter need to get that info
addObservers()
}
// MARK: - public functions
/// will send latest reading to all M5Stacks, only if it's less than 5 minutes old
/// - parameters:
/// - forM5Stack : if nil then latest reading will be sent to all connected M5Stacks, otherwise only to the specified M5Stack
public func sendLatestReading(forM5Stack m5Stack: M5Stack? = nil) {
// get reading of latest 5 minutes
let bgReadingToSend = bgReadingsAccessor.getLatestBgReadings(limit: 1, fromDate: Date(timeIntervalSinceNow: -5 * 60), forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// check that there's at least 1 reading available
guard bgReadingToSend.count >= 1 else {
trace("in sendLatestReading, there's no recent reading to send", log: self.log, type: .info)
return
}
if let m5Stack = m5Stack {
// send bgReading to the single m5Stack
_ = m5StackBluetoothTransmitter(forM5stack: m5Stack, createANewOneIfNecesssary: false)?.writeBgReadingInfo(bgReading: bgReadingToSend[0])
} else {
// send the reading to all M5Stacks
for m5StackBlueToothTransmitter in m5StacksBlueToothTransmitters.values {
if let transmitter = m5StackBlueToothTransmitter {
_ = transmitter.writeBgReadingInfo(bgReading: bgReadingToSend[0])
}
}
}
}
// MARK: - private helper functions
/// when user changes M5Stack related settings, then the transmitter need to get that info, add observers
private func addObservers() {
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiName1.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiName2.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiName3.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiPassword1.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiPassword2.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackWiFiPassword3.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.m5StackBlePassword.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.bloodGlucoseUnitIsMgDl.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutUrl.rawValue, options: .new, context: nil)
UserDefaults.standard.addObserver(self, forKeyPath: UserDefaults.Key.nightScoutAPIKey.rawValue, options: .new, context: nil)
}
/// send all parameters to m5StackBluetoothTransmitter
/// - returns:
/// successfully written all parameters or not
private func sendAllParameters(toM5Stack m5Stack : M5Stack) -> Bool {
guard let m5StackBluetoothTransmitterValue = m5StacksBlueToothTransmitters[m5Stack], let m5StackBluetoothTransmitter = m5StackBluetoothTransmitterValue else {
trace("in sendAllParameters, there's no m5StackBluetoothTransmitter for the specified m5Stack", log: self.log, type: .info)
return false
}
guard m5StackBluetoothTransmitter.isReadyToReceiveData else {
trace("in sendAllParameters, bluetoothTransmitter is not ready to receive data", log: self.log, type: .info)
return false
}
// initialise returnValue, result
var success = true
// send bloodglucoseunit
if !m5StackBluetoothTransmitter.writeBloodGlucoseUnit(isMgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl) {success = false}
// send textColor
if !m5StackBluetoothTransmitter.writeTextColor(textColor: M5StackColor(forUInt16: UInt16(m5Stack.textcolor)) ?? UserDefaults.standard.m5StackTextColor ?? ConstantsM5Stack.defaultTextColor) {success = false}
// send backGroundColor
if !m5StackBluetoothTransmitter.writeBackGroundColor(backGroundColor: M5StackColor(forUInt16: UInt16(m5Stack.backGroundColor)) ?? ConstantsM5Stack.defaultBackGroundColor ) {success = false}
// send rotation
if !m5StackBluetoothTransmitter.writeRotation(rotation: Int(m5Stack.rotation)) {success = false}
// send WiFiSSID's
if let wifiName = UserDefaults.standard.m5StackWiFiName1 {
if !m5StackBluetoothTransmitter.writeWifiName(name: wifiName, number: 1) {success = false}
}
if let wifiName = UserDefaults.standard.m5StackWiFiName2 {
if !m5StackBluetoothTransmitter.writeWifiName(name: wifiName, number: 2) {success = false}
}
if let wifiName = UserDefaults.standard.m5StackWiFiName3 {
if !m5StackBluetoothTransmitter.writeWifiName(name: wifiName, number: 3) {success = false}
}
// send WiFiPasswords
if let wifiPassword = UserDefaults.standard.m5StackWiFiPassword1 {
if !m5StackBluetoothTransmitter.writeWifiPassword(password: wifiPassword, number: 1) {success = false}
}
if let wifiPassword = UserDefaults.standard.m5StackWiFiPassword2 {
if !m5StackBluetoothTransmitter.writeWifiPassword(password: wifiPassword, number: 2) {success = false}
}
if let wifiPassword = UserDefaults.standard.m5StackWiFiPassword3 {
if !m5StackBluetoothTransmitter.writeWifiPassword(password: wifiPassword, number: 3) {success = false}
}
// send nightscout url
if let url = UserDefaults.standard.nightScoutUrl {
if !m5StackBluetoothTransmitter.writeNightScoutUrl(url: url) {success = false}
}
// send nightscout token
if let token = UserDefaults.standard.nightScoutAPIKey {
if !m5StackBluetoothTransmitter.writeNightScoutAPIKey(apiKey: token) {success = false}
}
// return success
return success
}
/// disconnect from M5Stack - and don't reconnect - set shouldconnect to false
func disconnect(fromM5stack m5Stack: M5Stack) {
// device should not reconnect after disconnecting
m5Stack.shouldconnect = false
// save in coredata
coreDataManager.saveChanges()
if let bluetoothTransmitter = m5StacksBlueToothTransmitters[m5Stack] {
if let bluetoothTransmitter = bluetoothTransmitter {
bluetoothTransmitter.disconnect(reconnectAfterDisconnect: false)
}
}
}
// MARK:- override observe function
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let keyPath = keyPath {
if let keyPathEnum = UserDefaults.Key(rawValue: keyPath) {
// first check keyValueObserverTimeKeeper
switch keyPathEnum {
case UserDefaults.Key.m5StackWiFiName1, UserDefaults.Key.m5StackWiFiName2, UserDefaults.Key.m5StackWiFiName3, UserDefaults.Key.m5StackWiFiPassword1, UserDefaults.Key.m5StackWiFiPassword2, UserDefaults.Key.m5StackWiFiPassword3, UserDefaults.Key.nightScoutAPIKey, UserDefaults.Key.nightScoutUrl, UserDefaults.Key.bloodGlucoseUnitIsMgDl :
// transmittertype change triggered by user, should not be done within 200 ms
if !keyValueObserverTimeKeeper.verifyKey(forKey: keyPathEnum.rawValue, withMinimumDelayMilliSeconds: 200) {
return
}
default:
break
}
// assuming it's a setting to be sent to all m5Stacks, loop through all M5Stacks
// only those settings that are to be handled by all M5Stacks need to be considered here
// loop through all m5StacksBlueToothTransmitters
for m5StackPair in m5StacksBlueToothTransmitters {
if let bluetoothTransmitter = m5StackPair.value {
// is value successfully written or not
var success = false
switch keyPathEnum {
case UserDefaults.Key.m5StackWiFiName1:
success = bluetoothTransmitter.writeWifiName(name: UserDefaults.standard.m5StackWiFiName1, number: 1)
case UserDefaults.Key.m5StackWiFiName2:
success = bluetoothTransmitter.writeWifiName(name: UserDefaults.standard.m5StackWiFiName2, number: 2)
case UserDefaults.Key.m5StackWiFiName3:
success = bluetoothTransmitter.writeWifiName(name: UserDefaults.standard.m5StackWiFiName3, number: 3)
case UserDefaults.Key.m5StackWiFiPassword1:
success = bluetoothTransmitter.writeWifiPassword(password: UserDefaults.standard.m5StackWiFiPassword1, number: 1)
case UserDefaults.Key.m5StackWiFiPassword2:
success = bluetoothTransmitter.writeWifiPassword(password: UserDefaults.standard.m5StackWiFiPassword2, number: 2)
case UserDefaults.Key.m5StackWiFiPassword3:
success = bluetoothTransmitter.writeWifiPassword(password: UserDefaults.standard.m5StackWiFiPassword3, number: 3)
case UserDefaults.Key.m5StackBlePassword:
// only if the password in the settings is not nil, and if the m5Stack doesn't have a password yet, then we will store it in the M5Stack.
if let blePassword = UserDefaults.standard.m5StackBlePassword, m5StackPair.key.blepassword == nil {
m5StackPair.key.blepassword = blePassword
}
case UserDefaults.Key.bloodGlucoseUnitIsMgDl:
success = bluetoothTransmitter.writeBloodGlucoseUnit(isMgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl)
case UserDefaults.Key.nightScoutAPIKey:
success = bluetoothTransmitter.writeNightScoutAPIKey(apiKey: UserDefaults.standard.nightScoutAPIKey)
case UserDefaults.Key.nightScoutUrl:
success = bluetoothTransmitter.writeNightScoutUrl(url: UserDefaults.standard.nightScoutUrl)
default:
break
}
// if not successful then set needs parameter update to true for the m5Stack
if !success {
m5StackPair.key.parameterUpdateNeeded = true
}
} else {
// seems to be an M5Stack which is currently disconnected - need to send parameterUpdateNeeded = true, so that all parameters will be sent as soon as reconnect occurs
m5StackPair.key.parameterUpdateNeeded = true
}
}
}
}
}
}
// MARK: - extensions
// MARK: extension BluetoothPeripheralManaging
extension BluetoothPeripheralManager: BluetoothPeripheralManaging {
/// to scan for a new M5SStack - callback will be called when a new M5Stack is found and connected
func startScanningForNewDevice(callback: @escaping (M5Stack) -> Void) {
callBackAfterDiscoveringDevice = callback
tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack = M5StackBluetoothTransmitter(m5Stack: nil, delegateFixed: self, blePassword: UserDefaults.standard.m5StackBlePassword)
_ = tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack?.startScanning()
}
/// stops scanning for new device
func stopScanningForNewDevice() {
if let tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack = tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack {
tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack.stopScanning()
self.tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack = nil
}
}
/// try to connect to the M5Stack
func connect(toM5Stack m5Stack: M5Stack) {
if let bluetoothTransmitter = m5StacksBlueToothTransmitters[m5Stack] {
// because m5StacksBlueToothTransmitters is a dictionary whereby the value is optional, bluetoothTransmitter is now optional, so we have to check again if it's nil or not
if let bluetoothTransmitter = bluetoothTransmitter {
// bluetoothtransmitter exists, but not connected, call the connect function
_ = bluetoothTransmitter.connect()
} else {
// this can be the case where initially shouldconnect was set to false, and user sets it to true via uiviewcontroller, uiviewcontroller calls this function, connect should automatially be initiated
let newBlueToothTransmitter = M5StackBluetoothTransmitter(m5Stack: m5Stack, delegateFixed: self, blePassword: UserDefaults.standard.m5StackBlePassword)
m5StacksBlueToothTransmitters[m5Stack] = newBlueToothTransmitter
}
} else {
// I don't think this code will be used, because value m5Stack should always be in m5StacksBlueToothTransmitters, anyway let's add it
let newBlueToothTransmitter = M5StackBluetoothTransmitter(m5Stack: m5Stack, delegateFixed: self, blePassword: UserDefaults.standard.m5StackBlePassword)
m5StacksBlueToothTransmitters[m5Stack] = newBlueToothTransmitter
}
}
/// returns the M5StackBluetoothTransmitter for the m5stack
/// - parameters:
/// - forM5Stack : the m5Stack for which bluetoothTransmitter should be returned
/// - createANewOneIfNecesssary : if bluetoothTransmitter is nil, then should one be created ?
func m5StackBluetoothTransmitter(forM5stack m5Stack: M5Stack, createANewOneIfNecesssary: Bool) -> M5StackBluetoothTransmitter? {
if let bluetoothTransmitter = m5StacksBlueToothTransmitters[m5Stack] {
if let bluetoothTransmitter = bluetoothTransmitter {
return bluetoothTransmitter
}
}
if createANewOneIfNecesssary {
let newTransmitter = M5StackBluetoothTransmitter(m5Stack: m5Stack, delegateFixed: self, blePassword: UserDefaults.standard.m5StackBlePassword)
m5StacksBlueToothTransmitters[m5Stack] = newTransmitter
return newTransmitter
}
return nil
}
/// deletes the M5Stack in coredata, and also the corresponding M5StackBluetoothTransmitter if there is one will be deleted
func deleteM5Stack(m5Stack: M5Stack) {
// if in dictionary remove it
if m5StacksBlueToothTransmitters.keys.contains(m5Stack) {
m5StacksBlueToothTransmitters[m5Stack] = (M5StackBluetoothTransmitter?).none
m5StacksBlueToothTransmitters.removeValue(forKey: m5Stack)
}
// delete in coredataManager
coreDataManager.mainManagedObjectContext.delete(m5Stack)
// save in coredataManager
coreDataManager.saveChanges()
}
/// - returns: the M5Stack's managed by this BluetoothPeripheralManager
func m5Stacks() -> [M5Stack] {
return Array(m5StacksBlueToothTransmitters.keys)
}
/// sets flag parameterUpdateNeeded for m5Stack to true
func updateNeeded(forM5Stack m5Stack: M5Stack) {
m5Stack.parameterUpdateNeeded = true
}
/// bluetoothtransmitter for this m5Stack will be deleted, as a result this will also disconnect the M5Stack
func setBluetoothTransmitterToNil(forM5Stack m5Stack: M5Stack) {
self.m5StacksBlueToothTransmitters[m5Stack] = (M5StackBluetoothTransmitter?).none
}
}
// MARK: extensions M5StackBluetoothDelegate
extension BluetoothPeripheralManager: M5StackBluetoothDelegate {
/// m5Stack is asking for an update of all parameters, send them
func isAskingForAllParameters(m5Stack: M5Stack) {
// send all parameters, if successful,then for this m5Stack we can set parameterUpdateNeeded to false
if sendAllParameters(toM5Stack: m5Stack) {
m5Stack.parameterUpdateNeeded = false
} else {
// failed, so we need to set parameterUpdateNeeded to true, so that next time it connects we will send all parameters
m5Stack.parameterUpdateNeeded = true
}
}
/// will be called if M5Stack is connected, and authentication was successful, BluetoothPeripheralManager can start sending data like parameter updates or bgreadings
func isReadyToReceiveData(m5Stack : M5Stack) {
// if the M5Stack needs new parameters, then send them
if m5Stack.parameterUpdateNeeded {
// send all parameters
if sendAllParameters(toM5Stack: m5Stack) {
m5Stack.parameterUpdateNeeded = false
}
}
// send latest reading
sendLatestReading(forM5Stack: m5Stack)
}
func didConnect(forM5Stack m5Stack: M5Stack?, address: String?, name: String?, bluetoothTransmitter : M5StackBluetoothTransmitter) {
guard tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack != nil else {
trace("in didConnect, tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack is nil, no further processing", log: self.log, type: .info)
return
}
// we're interested in new M5stack's which were scanned for, so that would mean m5Stack parameter would be nil, and if nil, then address should not yet be any of the known/stored M5Stack's
if m5Stack == nil, let address = address, let name = name {
// go through all the known m5Stacks and see if the address matches to any of them
for m5StackPair in m5StacksBlueToothTransmitters {
if m5StackPair.key.address == address {
// it's an already known m5Stack, not storing this, on the contrary disconnecting because maybe it's an m5stack already known for which user has preferred not to connect to
// If we're actually waiting for a new scan result, then there's an instance of M5StacksBlueToothTransmitter stored in tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack - but this one stopped scanning, so let's recreate an instance of M5StacksBlueToothTransmitter
tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack = M5StackBluetoothTransmitter(m5Stack: nil, delegateFixed: self, blePassword: UserDefaults.standard.m5StackBlePassword)
_ = tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack?.startScanning()
return
}
}
// looks like we haven't found the address in list of known M5Stacks, so it's a new M5Stack, stop the scanning
bluetoothTransmitter.stopScanning()
// create a new M5Stack with new peripheral's address and name
let newM5Stack = M5Stack(address: address, name: name, textColor: UserDefaults.standard.m5StackTextColor ?? ConstantsM5Stack.defaultTextColor, backGroundColor: ConstantsM5Stack.defaultBackGroundColor, rotation: ConstantsM5Stack.defaultRotation, brightness: 100, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// assign password stored in UserDefaults (might be nil)
newM5Stack.blepassword = UserDefaults.standard.m5StackBlePassword
// add to list of m5StacksBlueToothTransmitters
m5StacksBlueToothTransmitters[newM5Stack] = bluetoothTransmitter
// no need to keep a reference to the bluetothTransmitter, this is now stored in m5StacksBlueToothTransmitters
tempM5StackBlueToothTransmitterWhileScanningForNewM5Stack = nil
// assign n
bluetoothTransmitter.m5Stack = newM5Stack
// call the callback function
if let callBackAfterDiscoveringDevice = callBackAfterDiscoveringDevice {
callBackAfterDiscoveringDevice(newM5Stack)
self.callBackAfterDiscoveringDevice = nil
}
} else {
// if m5Stack is not nil, then this is a connect of one of the known M5Stacks
// nothing needed
}
}
func deviceDidUpdateBluetoothState(state: CBManagerState, forM5Stack m5Stack: M5Stack) {
trace("in deviceDidUpdateBluetoothState, no further action", log: self.log, type: .info)
}
func error(message: String) {
trace("in error, no further action", log: self.log, type: .info)
}
/// if a new ble password is received from M5Stack
func newBlePassWord(newBlePassword: String, forM5Stack m5Stack: M5Stack) {
trace("in newBlePassWord, storing the password in M5Stack", log: self.log, type: .info)
// possibily this is a new scanned m5stack, calling coreDataManager.saveChanges() but still the user may be in M5stackviewcontroller and decide not to save the m5stack, tant pis
m5Stack.blepassword = newBlePassword
coreDataManager.saveChanges()
}
/// did the app successfully authenticate towards M5Stack, if no, then disconnect will be done
///
func authentication(success: Bool, forM5Stack m5Stack:M5Stack) {
trace("in authentication with success = %{public}@", log: self.log, type: .info, success.description)
// if authentication not successful then disconnect and don't reconnect, user should verify password or reset the M5Stack, disconnect and set shouldconnect to false, permenantly (ie store in core data)
// disconnection is done because maybe another device is trying to connect to the M5Stack, need to make it free
// also set shouldConnect to false (note that this is also done in M5StackViewController if an instance of that exists, no issue, shouldConnect will be set to false two times
if !success {
m5Stack.shouldconnect = false
coreDataManager.saveChanges()
// disconnect
disconnect(fromM5stack: m5Stack)
}
}
/// there's no ble password set, user should set it in the settings - disconnect will be called, shouldconnect is set to false
func blePasswordMissing(forM5Stack m5Stack: M5Stack) {
trace("in blePasswordMissing", log: self.log, type: .info)
m5Stack.shouldconnect = false
coreDataManager.saveChanges()
// disconnect
disconnect(fromM5stack: m5Stack)
}
/// it's an M5Stack without password configured in the ini file. xdrip app has been requesting temp password to M5Stack but this was already done once. M5Stack needs to be reset. - disconnect will be called, shouldconnect is set to false
func m5StackResetRequired(forM5Stack m5Stack:M5Stack) {
trace("in m5StackResetRequired", log: self.log, type: .info)
m5Stack.shouldconnect = false
coreDataManager.saveChanges()
// disconnect
disconnect(fromM5stack: m5Stack)
}
/// did disconnect from M5Stack
func didDisconnect(forM5Stack m5Stack:M5Stack) {
// no further action, This is for UIViewcontroller's that also receive this info, means info can only be shown if this happens while user has one of the UIViewcontrollers open
trace("in didDisconnect", log: self.log, type: .info)
}
}

View File

@ -353,10 +353,10 @@
</objects>
<point key="canvasLocation" x="1599" y="566"/>
</scene>
<!--M5Stack Navigation Controller-->
<!--Bluetooth Peripheral Navigation Controller-->
<scene sceneID="H1e-vw-iaX">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="FwD-2z-GTm" userLabel="M5Stack Navigation Controller" customClass="BluetoothPeripheralNavigationController" customModule="xdrip" customModuleProvider="target" sceneMemberID="viewController">
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="FwD-2z-GTm" userLabel="Bluetooth Peripheral Navigation Controller" customClass="BluetoothPeripheralNavigationController" customModule="xdrip" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="" image="M5Stack" selectedImage="M5Stack" id="sgT-p5-hUt">
<color key="badgeColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tabBarItem>
@ -429,10 +429,10 @@
</objects>
<point key="canvasLocation" x="2582" y="565"/>
</scene>
<!--M5 Stacks View Controller-->
<!--Bluetooth Peripherals View Controller-->
<scene sceneID="0Ef-2k-ynF">
<objects>
<viewController id="E45-Z1-hcE" userLabel="M5 Stacks View Controller" customClass="BluetoothPeripheralsViewController" customModule="xdrip" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="E45-Z1-hcE" userLabel="Bluetooth Peripherals View Controller" customClass="BluetoothPeripheralsViewController" customModule="xdrip" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="i06-al-mW1">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -486,17 +486,17 @@
</navigationItem>
<connections>
<outlet property="tableView" destination="dMM-nl-Zfn" id="Z76-9m-jPM"/>
<segue destination="FJ0-eY-Fv0" kind="show" identifier="M5StacksToM5StackSegueIdentifier" id="BEa-Az-mem"/>
<segue destination="FJ0-eY-Fv0" kind="show" identifier="BluetoothPeripheralsToBluetoothPeripheralSegueIdentifier" id="BEa-Az-mem"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ajO-AG-xfd" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2582" y="-134"/>
</scene>
<!--M5 Stack View Controller-->
<!--Bluetooth Peripheral View Controller-->
<scene sceneID="fw3-II-GI1">
<objects>
<viewController id="FJ0-eY-Fv0" userLabel="M5 Stack View Controller" customClass="BluetoothPeripheralViewController" customModule="xdrip" customModuleProvider="target" sceneMemberID="viewController">
<viewController id="FJ0-eY-Fv0" userLabel="Bluetooth Peripheral View Controller" customClass="BluetoothPeripheralViewController" customModule="xdrip" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="0Ho-lg-UtI">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
@ -586,7 +586,7 @@
<outlet property="scanButtonOutlet" destination="L9f-Ab-Egz" id="z68-tK-4ql"/>
<outlet property="tableView" destination="82M-Ti-NV0" id="RQy-kH-bnc"/>
<outlet property="trashButtonOutlet" destination="N7o-Nz-l8g" id="Sz3-nt-e7r"/>
<segue destination="qbA-EA-RMD" kind="unwind" identifier="M5StackToBluetoothPeripheralsUnWindSegueIdentifier" unwindAction="unwindToBluetoothPeripheralsViewControllerWithSegue:" id="yPp-xT-8cB"/>
<segue destination="qbA-EA-RMD" kind="unwind" identifier="BluetoothPeripheralToBluetoothPeripheralsUnWindSegueIdentifier" unwindAction="unwindToBluetoothPeripheralsViewControllerWithSegue:" id="yPp-xT-8cB"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="kwG-Zz-t1q" userLabel="First Responder" sceneMemberID="firstResponder"/>

View File

@ -0,0 +1,10 @@
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"selectAliasText" = "Choose an alias for this Bluetooth Peripheral, the name will be shown in the app and is easier for you to recognize";
"aliasAlreadyExists" = "There is already a Bluetooth Peripheral with this alias";
"confirmDeletionPeripheral" = "Do you want to delete Bluetooth Peripheral with ";
"bluetoothPeripheralAlias" = "Alias";

View File

@ -0,0 +1,2 @@
"screenTitle" = "Bluetooth Devices";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1,6 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -1,14 +1 @@
"screenTitle" = "M5Stack";
"address" = "Address";
"status" = "Status";
"connected" = "connected";
"notConnected" = "not connected";
"alwaysconnect" = "Always Connect";
"donotconnect" = "Don't connect";
"authenticationFailureWarning" = "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ";
"blePasswordMissingWarning" = "You need to set the password in the Settings";
"m5StackResetRequiredWarning" = "M5Stack must be reset in order to generate a new temporary password. When done click ";
"m5StackAlias" = "Alias";
"selectAliasText" = "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize";
"userdefinedNameAlreadyExists" = "There is already an M5Stack with this name";
"confirmDeletionM5Stack" = "Do you want to delete M5Stack with ";

View File

@ -1,3 +0,0 @@
"screenTitle" = "M5Stack List";
"m5StackSoftWareHelpCellText" = "Where to find M5Stack software ?";
"m5StackSoftWareHelpText" = "Go to";

View File

@ -0,0 +1,47 @@
import Foundation
class Text_BluetoothPeripheralView {
static private let filename = "BluetoothPeripheralView"
static let address: String = {
return NSLocalizedString("address", tableName: filename, bundle: Bundle.main, value: "Address", comment: "when M5Stack is shown, title of the cell with the address")
}()
static let status: String = {
return NSLocalizedString("status", tableName: filename, bundle: Bundle.main, value: "Status", comment: "when Bluetooth Peripheral is shown, title of the cell with the status")
}()
static let connected: String = {
return NSLocalizedString("connected", tableName: filename, bundle: Bundle.main, value: "connected", comment: "when Bluetooth Peripheral is shown, connection status, connected")
}()
static let notConnected: String = {
return NSLocalizedString("notConnected", tableName: filename, bundle: Bundle.main, value: "Not Connected", comment: "when Bluetooth Peripheral is shown, connection status, not connected")
}()
static let alwaysConnect: String = {
return NSLocalizedString("alwaysconnect", tableName: filename, bundle: Bundle.main, value: "Always Connect", comment: "text in button top right, by clicking, user says that device should always try to connect")
}()
static let donotconnect: String = {
return NSLocalizedString("donotconnect", tableName: filename, bundle: Bundle.main, value: "Don't connect", comment: "text in button top right, this button will disable automatic connect")
}()
static let selectAliasText: String = {
return NSLocalizedString("selectAliasText", tableName: filename, bundle: Bundle.main, value: "Choose an alias for this Bluetooth Peripheral, the name will be shown in the app and is easier for you to recognize", comment: "Bluetooth Peripheral view, when user clicks alias field")
}()
static let aliasAlreadyExists: String = {
return NSLocalizedString("aliasAlreadyExists", tableName: filename, bundle: Bundle.main, value: "There is already a Bluetooth Peripheral with this alias", comment: "Bluetooth Peripheral view, when user clicks alias field")
}()
static let confirmDeletionBluetoothPeripheral: String = {
return NSLocalizedString("confirmDeletionPeripheral", tableName: filename, bundle: Bundle.main, value: "Do you want to delete Bluetooth Peripheral with ", comment: "Bluetooth Peripheral view, when user clicks the trash button - this is not the complete sentence, it will be followed either by 'name' or 'alias', depending on the availability of an alias")
}()
static let bluetoothPeripheralAlias: String = {
return NSLocalizedString("bluetoothPeripheralAlias", tableName: filename, bundle: Bundle.main, value: "Alias", comment: "BluetoothPeripheral view, this is a name of a BluetoothPeripheral assigned by the user, to recognize the device")
}()
}

View File

@ -0,0 +1,11 @@
import Foundation
class Texts_BluetoothPeripheralsView {
static private let filename = "BluetoothPeripheralsView"
static let screenTitle: String = {
return NSLocalizedString("screenTitle", tableName: filename, bundle: Bundle.main, value: "Bluetooth Devices", comment: "when Bluetooth Peripheral list is shown, title of the view")
}()
}

View File

@ -1,37 +1,14 @@
import Foundation
class Texts_M5StackView {
static private let filename = "M5StackView"
static let screenTitle: String = {
return NSLocalizedString("screenTitle", tableName: filename, bundle: Bundle.main, value: "M5Stack", comment: "when specific M5 stack is shown, screen title")
return NSLocalizedString("screenTitle", tableName: filename, bundle: Bundle.main, value: "M5Stack", comment: "when M5 stack list is shown, title of the view")
}()
static let address: String = {
return NSLocalizedString("address", tableName: filename, bundle: Bundle.main, value: "Address", comment: "when M5Stack is shown, title of the cell with the address")
}()
static let status: String = {
return NSLocalizedString("status", tableName: filename, bundle: Bundle.main, value: "Status", comment: "when M5Stack is shown, title of the cell with the status")
}()
static let connected: String = {
return NSLocalizedString("connected", tableName: filename, bundle: Bundle.main, value: "connected", comment: "when M5Stack is shown, connection status, connected")
}()
static let notConnected: String = {
return NSLocalizedString("notConnected", tableName: filename, bundle: Bundle.main, value: "Not Connected", comment: "when M5Stack is shown, connection status, not connected")
}()
static let alwaysConnect: String = {
return NSLocalizedString("alwaysconnect", tableName: filename, bundle: Bundle.main, value: "Always Connect", comment: "text in button top right, by clicking, user says that device should always try to connect")
}()
static let donotconnect: String = {
return NSLocalizedString("donotconnect", tableName: filename, bundle: Bundle.main, value: "Don't connect", comment: "text in button top right, this button will disable automatic connect")
}()
static let authenticationFailureWarning: String = {
return NSLocalizedString("authenticationFailureWarning", tableName: filename, bundle: Bundle.main, value: "Authentication to M5Stack Failed, either set the pre-configured password in the Settings, or, if the M5Stack does not have a preconfigured password then reset the M5Stack. M5Stack will disconnect now. You can make a new attempt by clicking ", comment: "in case M5Stack authentication failed")
}()
@ -43,21 +20,14 @@ class Texts_M5StackView {
static let m5StackResetRequiredWarning: String = {
return NSLocalizedString("m5StackResetRequiredWarning", tableName: filename, bundle: Bundle.main, value: "You need to reset the M5Stack in order to get a new temporary password. When done click'", comment: "in case M5Stack authentication failed, and M5Stack is generating a random password")
}()
static let m5StackSoftWareHelpCellText: String = {
return NSLocalizedString("m5StackSoftWareHelpCellText", tableName: filename, bundle: Bundle.main, value: "Where to find M5Stack software ?", comment: "In list of M5Stacks, the last line allows to show info where to find M5Stack software, this is the text in the cell")
}()
static let m5StackSoftWareHelpText: String = {
return NSLocalizedString("m5StackSoftWareHelpText", tableName: filename, bundle: Bundle.main, value: "Go to", comment: "this is the text shown when clicking the cell 'where to find M5Stack software'")
}()
static let m5StackAlias: String = {
return NSLocalizedString("m5StackAlias", tableName: filename, bundle: Bundle.main, value: "Alias", comment: "M5Stack view, this is a name of an M5Stack assigned by the user, to recognize the device")
}()
static let selectAliasText: String = {
return NSLocalizedString("selectAliasText", tableName: filename, bundle: Bundle.main, value: "Choose a name for this M5Stack, the name will be shown in the app and is easier for you to recognize", comment: "M5Stack view, when user clicks userdefinedname (alias) field")
}()
static let userdefinedNameAlreadyExists: String = {
return NSLocalizedString("userdefinedNameAlreadyExists", tableName: filename, bundle: Bundle.main, value: "There is already an M5Stack with this name", comment: "M5Stack view, when user clicks userdefinedname (alias) field")
}()
static let confirmDeletionM5Stack: String = {
return NSLocalizedString("confirmDeletionM5Stack", tableName: filename, bundle: Bundle.main, value: "Do you want to delete M5Stack with ", comment: "M5Stack view, when user clicks the trash button - this is not the complete sentence, it will be followed either by 'name' or 'alias', depending on the availability of a userdefined name")
}()
}

View File

@ -1,19 +0,0 @@
import Foundation
class Texts_M5StacksView {
static private let filename = "M5StacksView"
static let screenTitle: String = {
return NSLocalizedString("screenTitle", tableName: filename, bundle: Bundle.main, value: "M5Stack List", comment: "when M5 stack list is shown, title of the view")
}()
static let m5StackSoftWareHelpCellText: String = {
return NSLocalizedString("m5StackSoftWareHelpCellText", tableName: filename, bundle: Bundle.main, value: "Where to find M5Stack software ?", comment: "In list of M5Stacks, the last line allows to show info where to find M5Stack software, this is the text in the cell")
}()
static let m5StackSoftWareHelpText: String = {
return NSLocalizedString("m5StackSoftWareHelpText", tableName: filename, bundle: Bundle.main, value: "Go to", comment: "this is the text shown when clicking the cell 'where to find M5Stack software'")
}()
}

View File

@ -2,7 +2,7 @@ import Foundation
import CoreBluetooth
import os
final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
final class CGMG4xDripTransmitter: BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -43,37 +43,40 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
//assign transmitterId
self.transmitterId = transmitterID
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: CBUUID_Advertisement_G4, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_G4)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_G4, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_G4, startScanningAfterInit: CGMTransmitterType.dexcomG4.startScanningAfterInit())
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: CBUUID_Advertisement_G4, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_G4)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_G4, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_G4, startScanningAfterInit: CGMTransmitterType.dexcomG4.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: - functions
// MARK: - MARK: CBCentralManager overriden functions
// MARK: - BluetoothTransmitterDelegate functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
super.centralManager(central, didConnect: peripheral)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
trace("in peripheralDidUpdateNotificationStateFor", log: log, type: .info)
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
//check if value is not nil
guard let value = characteristic.value else {
trace("in peripheral didUpdateValueFor, characteristic.value is nil", log: log, type: .info)
@ -96,12 +99,12 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
//only for logging
let data = value.hexEncodedString()
trace("in peripheral didUpdateValueFor, data = %{public}@", log: log, type: .debug, data)
switch XdripResponseType(rawValue: value[1]) {
case .dataPacket?:
//process value and get result
let result = processxBridgeDataPacket(value: value)
// check transmitterid, if not correct write correct value and return
if let data = checkTransmitterId(receivedTransmitterId: result.transmitterID, expectedTransmitterId: self.transmitterId, log: log) {
trace(" in peripheralDidUpdateValueFor, sending transmitterid %{public}@ to xdrip ", log: log, type: .info, self.transmitterId)
@ -128,7 +131,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
trace(" in peripheral didUpdateValueFor, packet length is not 7, no further processing", log: log, type: .info)
return
}
//read txid
let receivedTransmitterId = decodeTxID(TxID: value.uint32(position: 2))
trace(" in peripheral didUpdateValueFor, received beaconPacket with txid %{public}@", log: log, type: .info, receivedTransmitterId)
@ -154,6 +157,7 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: transmitterBatteryInfo, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
}
}
}
// MARK: CGMTransmitter protocol functions

View File

@ -2,7 +2,7 @@ import Foundation
import CoreBluetooth
import os
class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
class CGMG5Transmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -136,14 +136,11 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
self.G5ResetRequested = false
// initialize - CBUUID_Receive_Authentication.rawValue and CBUUID_Write_Control.rawValue will probably not be used in the superclass
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: CBUUID_Advertisement_G5, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_G5)], CBUUID_ReceiveCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Receive_Authentication.rawValue, CBUUID_WriteCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Write_Control.rawValue, startScanningAfterInit: CGMTransmitterType.dexcomG5.startScanningAfterInit())
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: CBUUID_Advertisement_G5, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_G5)], CBUUID_ReceiveCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Receive_Authentication.rawValue, CBUUID_WriteCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Write_Control.rawValue, startScanningAfterInit: CGMTransmitterType.dexcomG5.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
//assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
// start scanning
_ = startScanning()
}
@ -183,112 +180,25 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
// MARK: CBCentralManager overriden functions
override func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if Date() < Date(timeInterval: 60, since: timeStampOfLastG5Reading) {
// will probably never come here because reconnect doesn't happen with scanning, hence diddiscover will never be called excep the very first time that an app tries to connect to a G5
trace("diddiscover peripheral, but last reading was less than 1 minute ago, will ignore", log: log, type: .info)
} else {
super.centralManager(central, didDiscover: peripheral, advertisementData: advertisementData, rssi: RSSI)
}
}
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
// if last reading was less than a minute ago, then no need to continue, otherwise continue with process by calling super.centralManager(central, didConnect: peripheral)
if Date() < Date(timeInterval: 60, since: timeStampOfLastG5Reading) {
trace("connected, but last reading was less than 1 minute ago", log: log, type: .info)
// don't disconnect here, keep the connection open, the transmitter will disconnect in a few seconds, assumption is that this will increase battery life
} else {
super.centralManager(central, didConnect: peripheral)
}
super.centralManagerDidUpdateState(central)
// to be sure waitingPairingConfirmation is reset to false
waitingPairingConfirmation = false
}
override func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
trace("didDiscoverCharacteristicsFor", log: log, type: .info)
// log error if any
if let error = error {
trace(" error: %{public}@", log: log, type: .error , error.localizedDescription)
}
if let characteristics = service.characteristics {
for characteristic in characteristics {
if let characteristicValue = CBUUID_Characteristic_UUID(rawValue: characteristic.uuid.uuidString) {
trace(" characteristic : %{public}@", log: log, type: .info, characteristicValue.description)
switch characteristicValue {
case .CBUUID_Backfill:
backfillCharacteristic = characteristic
case .CBUUID_Write_Control:
writeControlCharacteristic = characteristic
case .CBUUID_Communication:
communicationCharacteristic = characteristic
case .CBUUID_Receive_Authentication:
receiveAuthenticationCharacteristic = characteristic
trace(" calling setNotifyValue true", log: log, type: .info)
peripheral.setNotifyValue(true, for: characteristic)
}
} else {
trace(" characteristic UUID unknown : %{public}@", log: log, type: .error, characteristic.uuid.uuidString)
}
}
} else {
trace("characteristics is nil. There must be some error.", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions
/// to ask pairing
func initiatePairing() {
// assuming that the transmitter is effectively awaiting the pairing, otherwise this obviously won't work
sendPairingRequest()
}
/// to ask transmitter reset
func reset(requested:Bool) {
G5ResetRequested = requested
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
/// this transmitter does not support oop web
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
// MARK: BluetoothTransmitterDelegate functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
}
func centralManagerDidUpdateState(state: CBManagerState) {
// if status changed to poweredon, and if address = nil then superclass will not start the scanning
// but for DexcomG5 we can start scanning
if state == .poweredOn {
if (address() == nil) {
_ = startScanning()
if central.state == .poweredOn {
if (getAddress() == nil) {
_ = startScanning()
}
}
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
if waitingPairingConfirmation {
// device has requested a pairing request and is now in a status of verifying if pairing was successfull or not, this by doing setNotify to writeCharacteristic. If a disconnect occurs now, it means pairing has failed (probably because user didn't approve it
@ -300,9 +210,13 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
// inform delegate
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
trace("in peripheralDidUpdateNotificationStateFor", log: log, type: .info)
if let error = error {
@ -338,12 +252,15 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
} else {
trace(" characteristicValue is nil", log: log, type: .error)
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
guard let characteristic_UUID = CBUUID_Characteristic_UUID(rawValue: characteristic.uuid.uuidString) else {
trace("in peripheralDidUpdateValueFor, unknown characteristic received with uuid = %{public}@", log: log, type: .error, characteristic.uuid.uuidString)
trace("in peripheralDidUpdateValueFor, unknown characteristic received with uuid = %{public}@", log: log, type: .error, characteristic.uuid.uuidString)
return
}
@ -380,14 +297,14 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
cgmTransmitterDelegate?.cgmTransmitterNeedsPairing()
} else {
// subscribe to writeControlCharacteristic
if let writeControlCharacteristic = writeControlCharacteristic {
setNotifyValue(true, for: writeControlCharacteristic)
} else {
trace(" writeControlCharacteristic is nil, can not set notifyValue", log: log, type: .error)
}
}
} else {
@ -445,8 +362,8 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
//if reset was done recently, less than 5 minutes ago, then ignore the reading
if Date() < Date(timeInterval: 5 * 60, since: timeStampTransmitterReset) {
trace(" last transmitter reset was less than 5 minutes ago, ignoring this reading", log: log, type: .info)
//} else if sensorDataRxMessage.unfiltered == 0.0 {
// trace(" sensorDataRxMessage.unfiltered = 0.0, ignoring this reading", log: log, type: .info)
//} else if sensorDataRxMessage.unfiltered == 0.0 {
// trace(" sensorDataRxMessage.unfiltered = 0.0, ignoring this reading", log: log, type: .info)
} else {
if Date() < Date(timeInterval: 60, since: timeStampOfLastG5Reading) {
// should probably never come here because this check is already done at connection time
@ -508,8 +425,100 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
trace(" characteristic.value is nil", log: log, type: .error)
}
}
}
override func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if Date() < Date(timeInterval: 60, since: timeStampOfLastG5Reading) {
// will probably never come here because reconnect doesn't happen with scanning, hence diddiscover will never be called excep the very first time that an app tries to connect to a G5
trace("diddiscover peripheral, but last reading was less than 1 minute ago, will ignore", log: log, type: .info)
} else {
super.centralManager(central, didDiscover: peripheral, advertisementData: advertisementData, rssi: RSSI)
}
}
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// not calling super.didconnect here
// if last reading was less than a minute ago, then no need to continue, otherwise continue with process by calling super.centralManager(central, didConnect: peripheral)
if Date() < Date(timeInterval: 60, since: timeStampOfLastG5Reading) {
trace("connected, but last reading was less than 1 minute ago", log: log, type: .info)
// don't disconnect here, keep the connection open, the transmitter will disconnect in a few seconds, assumption is that this will increase battery life
} else {
super.centralManager(central, didConnect: peripheral)
}
// to be sure waitingPairingConfirmation is reset to false
waitingPairingConfirmation = false
}
override func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
// not using super.didDiscoverCharacteristicsFor here
trace("didDiscoverCharacteristicsFor", log: log, type: .info)
// log error if any
if let error = error {
trace(" error: %{public}@", log: log, type: .error , error.localizedDescription)
}
if let characteristics = service.characteristics {
for characteristic in characteristics {
if let characteristicValue = CBUUID_Characteristic_UUID(rawValue: characteristic.uuid.uuidString) {
trace(" characteristic : %{public}@", log: log, type: .info, characteristicValue.description)
switch characteristicValue {
case .CBUUID_Backfill:
backfillCharacteristic = characteristic
case .CBUUID_Write_Control:
writeControlCharacteristic = characteristic
case .CBUUID_Communication:
communicationCharacteristic = characteristic
case .CBUUID_Receive_Authentication:
receiveAuthenticationCharacteristic = characteristic
trace(" calling setNotifyValue true", log: log, type: .info)
peripheral.setNotifyValue(true, for: characteristic)
}
} else {
trace(" characteristic UUID unknown : %{public}@", log: log, type: .error, characteristic.uuid.uuidString)
}
}
} else {
trace("characteristics is nil. There must be some error.", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions
/// to ask pairing
func initiatePairing() {
// assuming that the transmitter is effectively awaiting the pairing, otherwise this obviously won't work
sendPairingRequest()
}
/// to ask transmitter reset
func reset(requested:Bool) {
G5ResetRequested = requested
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
/// this transmitter does not support oop web
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
// MARK: helper functions
/// sends SensorTxMessage to transmitter

View File

@ -11,7 +11,7 @@ protocol CGMTransmitter {
/// get device address, cgmtransmitters should also derive from BlueToothTransmitter, hence no need to implement this function
///
/// this function is implemented in class BluetoothTransmitter.swift, it's not necessary for transmitter types to implement this function (as new transmitterType class conform to protocol CGMTransmitter but also extend the BluetoothTransmitter class
func address() -> String?
func getAddress() -> String?
/// get device name, cgmtransmitters should also derive from BlueToothTransmitter, hence no need to implement this function
///

View File

@ -91,14 +91,11 @@ class CGMBluconTransmitter: BluetoothTransmitter {
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())
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(), bluetoothTransmitterDelegate: nil)
//assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: - private helper functions
@ -231,52 +228,43 @@ class CGMBluconTransmitter: BluetoothTransmitter {
/// this transmitter does not support oop web
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}
}
// MARK: - overriden BluetoothTransmitter functions
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
extension CGMBluconTransmitter: CGMTransmitter {
func initiatePairing() {
// nothing to do, Blucon keeps on reconnecting, resulting in continous pairing request
return
}
func reset(requested: Bool) {
// no reset supported for blucon
return
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
}
extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
func centralManagerDidConnect(address: String?, name: String?) {
super.centralManager(central, didConnect: peripheral)
trace("in centralManagerDidConnect", log: log, type: .info)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
trace("in centralManagerDidDisconnectPeripheral", log: log, type: .info)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
trace("in peripheralDidUpdateNotificationStateFor", log: log, type: .info)
// check if error occurred
if let error = error {
// no need to log the error, it's already logged in BluetoothTransmitter
// check if it's an encryption error, if so call delegate
@ -292,19 +280,22 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
waitingSuccessfulPairing = false
}
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
// log the received characteristic value
trace("in peripheralDidUpdateValueFor with characteristic UUID = %{public}@", log: log, type: .info, characteristic.uuid.uuidString)
// this is only applicable the very first time that blucon connects and pairing is done
if waitingSuccessfulPairing {
cgmTransmitterDelegate?.successfullyPaired()
waitingSuccessfulPairing = false
}
// check if error occured
if let error = error {
trace(" error: %{public}@", log: log, type: .error , error.localizedDescription)
@ -330,24 +321,24 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
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)
// 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)
// 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
trace(" 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 detect sensor, call delegate
@ -390,14 +381,14 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
} else {
trace(" 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:
@ -409,12 +400,12 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
// 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)
// set to false to be sure
shouldSendUnknown1CommandAfterReceivingBluconAckResponse = false
// send unknown1Command
sendCommandToBlucon(opcode: BluconTransmitterOpCode.unknown1Command)
} else {
trace(" no further processing, Blucon is sleeping now and should send a new reading in 5 minutes", log: log, type: .info)
@ -424,33 +415,33 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
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
@ -458,11 +449,11 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
sendCommandToBlucon(opcode: .sleep)
}
case .singleBlockInfoResponsePrefix:
if !waitingForGlucoseData {
// get blockNumber and compose command
let commandToSend = BluconTransmitterOpCode.singleBlockInfoPrefix.rawValue + blockNumberForNowGlucoseData(input: value)
@ -478,12 +469,12 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
trace(" 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
@ -503,7 +494,7 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
if abs(timeStampLastBgReading.timeIntervalSinceNow) < 30 {
trace(" last reading less than 30 seconds old, ignoring this one", log: log, type: .info)
} else {
trace(" creating glucoseValue", log: log, type: .info)
// create glucose reading with timestamp now
@ -515,31 +506,50 @@ extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
let glucoseData = GlucoseData(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 {
trace("in peripheral didUpdateValueFor, value is nil, no further processing", log: log, type: .error)
}
}
}
extension CGMBluconTransmitter: CGMTransmitter {
func initiatePairing() {
// nothing to do, Blucon keeps on reconnecting, resulting in continous pairing request
return
}
func reset(requested: Bool) {
// no reset supported for blucon
return
}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
}

View File

@ -2,7 +2,7 @@ import Foundation
import os
import CoreBluetooth
class CGMBlueReaderTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
class CGMBlueReaderTransmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -39,35 +39,45 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegat
// assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_BlueReader)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_BlueReader, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_BlueReader, startScanningAfterInit: CGMTransmitterType.blueReader.startScanningAfterInit())
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_BlueReader)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_BlueReader, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_BlueReader, startScanningAfterInit: CGMTransmitterType.blueReader.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
}
// MARK: - BluetoothTransmitterDelegate functions
// MARK: - overriden BluetoothTransmitter functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
super.centralManager(central, didConnect: peripheral)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
trace("in peripheral didUpdateValueFor", log: log, type: .info)
@ -97,7 +107,7 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegat
trace(" failed to convert rawDataAsString to double", log: log, type: .error)
return
}
// if there's more than one field, then the second field is battery level
// there could be 2 fields or more
//var batteryLevelAsString:String? = nil
@ -120,6 +130,7 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegat
} else {
trace(" value is nil, no further processing", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions
@ -135,8 +146,7 @@ class CGMBlueReaderTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegat
func reset(requested:Bool) {}
/// this transmitter does not support oopWeb
func setWebOOPEnabled(enabled: Bool) {
}
func setWebOOPEnabled(enabled: Bool) {}
/// this transmitter does not support oop web
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {}

View File

@ -2,7 +2,7 @@ import Foundation
import CoreBluetooth
import os
class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
class CGMBubbleTransmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -90,10 +90,8 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
self.oopWebToken = oopWebToken
self.oopWebSite = oopWebSite
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_Bubble)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Bubble, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Bubble, startScanningAfterInit: CGMTransmitterType.Bubble.startScanningAfterInit())
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_Bubble)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Bubble, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Bubble, startScanningAfterInit: CGMTransmitterType.Bubble.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: - public functions
@ -107,31 +105,45 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
}
}
// MARK: - BluetoothTransmitterDelegate functions
// MARK: - overriden BluetoothTransmitter functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
super.centralManager(central, didConnect: peripheral)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
if error == nil && characteristic.isNotifying {
_ = sendStartReadingCommmand()
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
if let value = characteristic.value {
@ -150,10 +162,10 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
let hardware = value[2].description + ".0"
let firmware = value[2].description + "." + value[3].description
let batteryPercentage = Int(value[4])
// send hardware, firmware and batteryPercentage to delegate
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: nil, sensorTimeInMinutes: nil, firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
// confirm receipt
_ = writeDataToPeripheral(data: Data([0x02, 0x00, 0x00, 0x00, 0x00, 0x2B]), type: .withoutResponse)
@ -168,7 +180,7 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
if (Crc.LibreCrc(data: &rxBuffer, headerOffset: bubbleHeaderLength)) {
if let libreSensorSerialNumber = LibreSensorSerialNumber(withUID: Data(rxBuffer.subdata(in: 0..<8))) {
// verify serial number and if changed inform delegate
if libreSensorSerialNumber.serialNumber != sensorSerialNumber {
@ -185,16 +197,16 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
// inform delegate about new sensorSerialNumber
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &emptyArray, transmitterBatteryInfo: nil, sensorState: nil, sensorTimeInMinutes: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: sensorSerialNumber)
}
}
LibreDataParser.libreDataProcessor(sensorSerialNumber: sensorSerialNumber, webOOPEnabled: webOOPEnabled, oopWebSite: oopWebSite, oopWebToken: oopWebToken, libreData: (rxBuffer.subdata(in: bubbleHeaderLength..<(344 + bubbleHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: nil, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, timeStampLastBgReading: timeStampLastBgReading, completionHandler: {(timeStampLastBgReading:Date) in
self.timeStampLastBgReading = timeStampLastBgReading
})
//reset the buffer
resetRxBuffer()
@ -208,8 +220,9 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
} else {
trace("in peripheral didUpdateValueFor, value is nil, no further processing", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions
/// to ask pairing - empty function because Bubble doesn't need pairing

View File

@ -2,7 +2,7 @@ import Foundation
import os
import CoreBluetooth
class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
class CGMDroplet1Transmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -39,35 +39,39 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_Droplet)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Droplet, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Droplet, startScanningAfterInit: CGMTransmitterType.Droplet1.startScanningAfterInit())
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_Droplet)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Droplet, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Droplet, startScanningAfterInit: CGMTransmitterType.Droplet1.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
}
// MARK: - BluetoothTransmitterDelegate functions
// MARK: - overriden BluetoothTransmitter functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
super.centralManager(central, didConnect: peripheral)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
trace("in peripheral didUpdateValueFor", log: log, type: .info)
@ -88,7 +92,7 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
trace(" there's less than 3 spaces", log: log, type: .error)
return
}
// get first field
let firstField = String(valueAsString[valueAsString.startIndex..<indexesOfSplitter[0]])
@ -97,7 +101,7 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
cgmTransmitterDelegate?.sensorNotDetected()
return
}
// first field : first digits are rawvalue, to be multiplied with 100, last two digits are sensor type indicator, 10=L1, 20=L2, 30=US 14 day, 40=Lpro/h
let rawValueAsString = firstField[0..<(firstField.count - 2)] + "00"
let sensorTypeIndicator = firstField[(firstField.count - 2)..<firstField.count]
@ -108,7 +112,7 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
trace(" failed to convert rawValueAsString to double", log: log, type: .error)
return
}
// third field is battery percentage, stop if convert to Int fails
guard let batteryPercentage = Int(String(valueAsString[valueAsString.index(after: indexesOfSplitter[1])..<indexesOfSplitter[2]])) else {
trace(" failed to convert batteryPercentage field to Int", log: log, type: .error)
@ -120,7 +124,7 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
trace(" failed to convert sensorTimeInMinutes field to Int", log: log, type: .error)
return
}
// send to delegate
var glucoseDataArray = [GlucoseData(timeStamp: Date(), glucoseLevelRaw: rawValueAsDouble)]
cgmTransmitterDelegate?.cgmTransmitterInfoReceived(glucoseData: &glucoseDataArray, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), sensorState: nil, sensorTimeInMinutes: sensorTimeInMinutes * 10, firmware: nil, hardware: nil, hardwareSerialNumber: nil, bootloader: nil, sensorSerialNumber: nil)
@ -128,6 +132,7 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
} else {
trace(" value is nil, no further processing", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions

View File

@ -2,7 +2,7 @@ import Foundation
import CoreBluetooth
import os
class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
class CGMGNSEntryTransmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -105,40 +105,42 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
self.timeStampLastBgReadingInMinutes = timeStampLastBgReading.toMillisecondsAsDouble()/1000/60
// initialize
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_GNWService), CBUUID(string: CBUUID_BatteryService), CBUUID(string: CBUUID_DeviceInformationService)], CBUUID_ReceiveCharacteristic: CBUUID_Characteristic_UUID.CBUUID_GNW_Notify.rawValue, CBUUID_WriteCharacteristic: CBUUID_Characteristic_UUID.CBUUID_GNW_Write.rawValue, startScanningAfterInit: CGMTransmitterType.GNSentry.startScanningAfterInit())
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_GNWService), CBUUID(string: CBUUID_BatteryService), CBUUID(string: CBUUID_DeviceInformationService)], CBUUID_ReceiveCharacteristic: CBUUID_Characteristic_UUID.CBUUID_GNW_Notify.rawValue, CBUUID_WriteCharacteristic: CBUUID_Characteristic_UUID.CBUUID_GNW_Write.rawValue, startScanningAfterInit: CGMTransmitterType.GNSentry.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
//assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: BluetoothTransmitterDelegate functions
// MARK: - overriden BluetoothTransmitter functions
func centralManagerDidConnect(address:String?, name:String?) {
trace("in centralManagerDidConnect", log: log, type: .info)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
super.centralManager(central, didConnect: peripheral)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
trace("in centralManagerDidDisconnectPeripheral", log: log, type: .info)
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
trace("in peripheralDidUpdateNotificationStateFor", log: log, type: .info)
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
// log the received characteristic value
trace("in peripheralDidUpdateValueFor with characteristic UUID = %{public}@, matches characteristic name %{public}@", log: log, type: .info, characteristic.uuid.uuidString, receivedCharacteristicUUIDToCharacteristic(characteristicUUID: characteristic.uuid.uuidString)?.description ?? "not available")
@ -181,7 +183,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
var valueDecoded = XORENC(inD: [UInt8](value))
let valueDecodedAsHexString = Data(valueDecoded).hexEncodedString()
trace(" in peripheralDidUpdateValueFor, GNW Notify with hex value = %{public}@", log: log, type: .info , valueDecodedAsHexString)
trace(" in peripheralDidUpdateValueFor, GNW Notify with hex value = %{public}@", log: log, type: .info , valueDecodedAsHexString)
// reading status, as per GNSEntry documentation
let readingStatus = getIntAtPosition(numberOfBytes: 1, position: 0, data: &valueDecoded)
@ -218,10 +220,10 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
loop: while Int(7.0 + i * 2.0) < valueDecoded.count - 1 && i < amountOfPerMinuteReadings + amountOfPer15MinuteReadings {
// timestamp of the reading in minutes, counting from 1 1 1970
let readingTimeStampInMinutes:Double = currentTimeInMinutes - (i < amountOfPerMinuteReadings ? i : i * 15.0)
// get the reading value (mgdl)
let readingValueInMgDl = getIntAtPosition(numberOfBytes: 2, position: Int(7 + i * 2), data: &valueDecoded)
//new reading should be at least 30 seconds younger than timeStampLastBgReadingStoredInDatabase
if readingTimeStampInMinutes > ((timeStampLastBgReadingInMinutes * 2) + 1)/2 {
@ -233,7 +235,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
timeStampLastAddedGlucoseDataInMinutes = readingTimeStampInMinutes
}
}
} else {
break loop
}
@ -251,6 +253,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
}
}
}
}
// MARK: CGMTransmitter protocol functions

View File

@ -2,7 +2,7 @@ import Foundation
import CoreBluetooth
import os
class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTransmitter {
class CGMMiaoMiaoTransmitter:BluetoothTransmitter, CGMTransmitter {
// MARK: - properties
@ -86,10 +86,8 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
self.oopWebToken = oopWebToken
self.oopWebSite = oopWebSite
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_MiaoMiao)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_MiaoMiao, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_MiaoMiao, startScanningAfterInit: CGMTransmitterType.miaomiao.startScanningAfterInit())
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service_MiaoMiao)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_MiaoMiao, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_MiaoMiao, startScanningAfterInit: CGMTransmitterType.miaomiao.startScanningAfterInit(), bluetoothTransmitterDelegate: nil)
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: - public functions
@ -103,31 +101,45 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
}
}
// MARK: - BluetoothTransmitterDelegate functions
// MARK: - overriden BluetoothTransmitter functions
func centralManagerDidConnect(address:String?, name:String?) {
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
override func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
super.centralManager(central, didConnect: peripheral)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: deviceAddress, name: deviceName)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
super.centralManagerDidUpdateState(central)
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
if error == nil && characteristic.isNotifying {
_ = sendStartReadingCommand()
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
if let value = characteristic.value {
@ -155,7 +167,7 @@ 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])
LibreDataParser.libreDataProcessor(sensorSerialNumber: LibreSensorSerialNumber(withUID: Data(rxBuffer.subdata(in: 5..<13)))?.serialNumber, webOOPEnabled: webOOPEnabled, oopWebSite: oopWebSite, oopWebToken: oopWebToken, libreData: (rxBuffer.subdata(in: miaoMiaoHeaderLength..<(344 + miaoMiaoHeaderLength))), cgmTransmitterDelegate: cgmTransmitterDelegate, transmitterBatteryInfo: TransmitterBatteryInfo.percentage(percentage: batteryPercentage), firmware: firmware, hardware: hardware, hardwareSerialNumber: nil, bootloader: nil, timeStampLastBgReading: timeStampLastBgReading, completionHandler: {(timeStampLastBgReading:Date) in
self.timeStampLastBgReading = timeStampLastBgReading
@ -218,6 +230,8 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
} else {
trace("in peripheral didUpdateValueFor, value is nil, no further processing", log: log, type: .error)
}
}
// MARK: CGMTransmitter protocol functions

View File

@ -0,0 +1,71 @@
import Foundation
/// Every class that represents a bluetooth peripheral type needs to conform to this protocol. Classes being core data classes, ie NSManagedObject classes. Example M5Stack conforms to this protocol.
///
protocol BluetoothPeripheral {
// MARK:- functions related to generic parameters like address, alias, devicename,..
/// what type of peripheral is this
func bluetoothPeripheralType() -> BluetoothPeripheralType
/// mac address of the peripheral
///
/// implementation will always be the same, ie return the address
func getAddress() -> String
/// userdefined alias of the peripheral
///
/// implementation will always be the same, ie the alias
func getAlias() -> String?
/// device name as given by the peripheral
func getDeviceName() -> String
/// set the alias
func setAlias(_ value: String?)
// MARK:- connection related functions
/// tells the app that it should not try to connect to this bluetoothperipheral
///
/// it's just an internal boolean value that is stored
func dontTryToConnectToThisBluetoothPeripheral()
/// tells the app that it should always try to connect to this bluetoothperipheral
///
/// it's just an internal boolean value that is stored
func alwaysTryToConnectToThisBluetoothPeripheral()
/// should xdrip try to connect yes or no ?
///
/// it's just an internal boolean value that is stored
func shouldXdripTryToConnectToThisBluetoothPeripheral() -> Bool
/// when peripheral reconnects, app should not send list of parameters
///
/// - For example in case of M5Stack, user may change rotation or background color while it's not connected.
/// - In that case parameterUpdateNeededAtNextConnect will be called.
/// - Then later, when reconnecting isParameterUpdateNeededAtNextConnect tells us that app should send all parameters to the M5Stack.
/// - After that a call is made to parameterUpdateNotNeededAtNextConnect
func parameterUpdateNotNeededAtNextConnect()
/// when peripheral reconnects, app should not send list of parameters
///
/// - For example in case of M5Stack, user may change rotation or background color while it's not connected.
/// - In that case parameterUpdateNeededAtNextConnect will be called.
/// - Then later, when reconnecting isParameterUpdateNeededAtNextConnect tells us that app should send all parameters to the M5Stack.
/// - After that a call is made to parameterUpdateNotNeededAtNextConnect
func parameterUpdateNeededAtNextConnect()
/// when peripheral reconnects, should app send a list of parameters ?
///
/// - For example in case of M5Stack, user may change rotation or background color while it's not connected.
/// - In that case parameterUpdateNeededAtNextConnect will be called.
/// - Then later, when reconnecting isParameterUpdateNeededAtNextConnect tells us that app should send all parameters to the M5Stack.
/// - After that a call is made to parameterUpdateNotNeededAtNextConnect
func isParameterUpdateNeededAtNextConnect() -> Bool
}

View File

@ -0,0 +1,20 @@
import Foundation
enum BluetoothPeripheralType: String, CaseIterable {
/// M5Stack
case M5Stack = "M5Stack"
func getViewModel() -> BluetoothPeripheralViewModel {
switch self {
case .M5Stack:
return M5StackBluetoothPeripheralViewModel()
}
}
}

View File

@ -2,21 +2,23 @@ import Foundation
import CoreBluetooth
import os
/// generic bluetoothtransmitter class that handles scanning, connect, discover services, discover characteristics, subscribe to receive characteristic, reconnect.
/// Generic bluetoothtransmitter class that handles scanning, connect, discover services, discover characteristics, subscribe to receive characteristic, reconnect. This class is a base class for specific type of transmitters.
///
/// The class assumes that the transmitter has a receive and transmit characterisitc (which is mostly the case)
/// The class assumes that the transmitter has a receive and transmit characterisitc (which is mostly the case) - incase there's more characteristics to be processed, then the derived class will need to override didUpdateValueFor function
class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
// MARK: - properties
// MARK: - public properties
/// the BluetoothTransmitterDelegate
public weak var bluetoothTransmitterDelegate:BluetoothTransmitterDelegate?
/// variable : it can get a new value during app run, will be used by rootviewcontroller's that want to receive info
public weak var variableBluetoothTransmitterDelegate: BluetoothTransmitterDelegate?
// MARK: - private properties
/// the address of the transmitter. If nil then transmitter never connected, so we don't know the name.
private var deviceAddress:String?
/// the address of the transmitter. If nil then transmitter never connected, so we don't know the address.
private(set) var deviceAddress:String?
/// the name of the transmitter. If nil then transmitter never connected, so we don't know the name
private var deviceName:String?
private(set) var deviceName:String?
/// uuid used for scanning, can be empty string, if empty string then scan all devices - only possible if app is in foreground
private let CBUUID_Advertisement:String?
@ -58,11 +60,14 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
private var receiveCharacteristic:CBCharacteristic?
/// used in BluetoothTransmitter class, eg if after calling discoverServices new method is called and time is exceed, then cancel connection
let maxTimeToWaitForPeripheralResponse = 5.0
private let maxTimeToWaitForPeripheralResponse = 5.0
/// should the app try to reconnect after disconnect?
private var reconnectAfterDisconnect:Bool = true
/// fixed : it will be set during init and not change, there's also a variable one, named variableBluetoothTransmitterDelegate
private(set) weak var fixedBluetoothTransmitterDelegate: BluetoothTransmitterDelegate?
// MARK: - Initialization
/// - parameters:
@ -74,7 +79,8 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// - servicesCBUUIDs: service uuid's
/// - CBUUID_ReceiveCharacteristic: receive characteristic uuid
/// - CBUUID_WriteCharacteristic: write characteristic uuid
init(addressAndName:BluetoothTransmitter.DeviceAddressAndName, CBUUID_Advertisement:String?, servicesCBUUIDs:[CBUUID], CBUUID_ReceiveCharacteristic:String, CBUUID_WriteCharacteristic:String, startScanningAfterInit:Bool) {
/// - delegate : a
init(addressAndName:BluetoothTransmitter.DeviceAddressAndName, CBUUID_Advertisement:String?, servicesCBUUIDs:[CBUUID], CBUUID_ReceiveCharacteristic:String, CBUUID_WriteCharacteristic:String, startScanningAfterInit:Bool, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate?) {
switch addressAndName {
@ -99,6 +105,9 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
//initialize timeStampLastStatusUpdate
timeStampLastStatusUpdate = Date()
// assign delegate
self.fixedBluetoothTransmitterDelegate = bluetoothTransmitterDelegate
super.init()
initialize()
@ -153,6 +162,14 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
self.centralManager?.stopScan()
}
/// is the transmitter currently scanning or not
func isScanning() -> Bool {
if let centralManager = centralManager {
return centralManager.isScanning
}
return false
}
/// start bluetooth scanning for device
func startScanning() -> BluetoothTransmitter.startScanningResult {
@ -342,10 +359,13 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
timeStampLastStatusUpdate = Date()
trace("connected to peripheral with name %{public}@, will discover services", log: log, type: .info, deviceName ?? "'unknown'")
trace("connected to peripheral with name %{public}@", log: log, type: .info, deviceName ?? "'unknown'")
fixedBluetoothTransmitterDelegate?.didConnectTo(bluetoothTransmitter: self)
variableBluetoothTransmitterDelegate?.didConnectTo(bluetoothTransmitter: self)
peripheral.discoverServices(servicesCBUUIDs)
bluetoothTransmitterDelegate?.centralManagerDidConnect(address: deviceAddress, name: deviceName)
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
@ -359,13 +379,15 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
centralManager?.connect(peripheral, options: nil)
bluetoothTransmitterDelegate?.centralManagerDidFailToConnect(error: error)
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
timeStampLastStatusUpdate = Date()
trace("in centralManagerDidUpdateState, for peripheral with name %{public}@, new state is %{public}@", log: log, type: .info, deviceName ?? "'unknown'", "\(central.state.toString())")
fixedBluetoothTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state, bluetoothTransmitter: self)
variableBluetoothTransmitterDelegate?.deviceDidUpdateBluetoothState(state: central.state, bluetoothTransmitter: self)
/// in case status changed to powered on and if device address known then try either to retrieveperipherals, or if that doesn't succeed, start scanning
if central.state == .poweredOn, reconnectAfterDisconnect {
@ -377,20 +399,20 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
}
}
bluetoothTransmitterDelegate?.centralManagerDidUpdateState(state: central.state)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
timeStampLastStatusUpdate = Date()
trace(" didDisconnect peripheral with name %{public}@", log: log, type: .info , deviceName ?? "'unknown'")
fixedBluetoothTransmitterDelegate?.didDisconnectFrom(bluetoothTransmitter: self)
variableBluetoothTransmitterDelegate?.didDisconnectFrom(bluetoothTransmitter: self)
if let error = error {
trace(" error: %{public}@", log: log, type: .error , error.localizedDescription)
}
bluetoothTransmitterDelegate?.centralManagerDidDisconnectPeripheral(error: error)
// check if automatic reconnect is needed or not
if !reconnectAfterDisconnect {
@ -473,9 +495,6 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
trace("didUpdateNotificationStateFor for peripheral with name %{public}@, characteristic %{public}@, characteristic description %{public}@, error = %{public}@", log: log, type: .error, deviceName ?? "'unkonwn'", String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
}
// call delegate
bluetoothTransmitterDelegate?.peripheralDidUpdateNotificationStateFor(characteristic: characteristic, error: error)
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
@ -483,15 +502,14 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
if let error = error {
trace("didUpdateValueFor for peripheral with name %{public}@, characteristic %{public}@, characteristic description %{public}@, error = %{public}@, no further processing", log: log, type: .error, deviceName ?? "'unknown'", String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
} else {
bluetoothTransmitterDelegate?.peripheralDidUpdateValueFor(characteristic: characteristic, error: error)
}
}
// MARK: methods to get address and name
/// read device address
func address() -> String? {
func getAddress() -> String? {
return deviceAddress
}

View File

@ -1,40 +1,20 @@
import Foundation
import CoreBluetooth
/// Defines functions similar as CBCentralManagerDelegate, CBPeripheralDelegate, with a different name and signature
///
/// Goal is that delegates that conform to this protocol have a way to receive information about bluetooth activities. Example centralManager didDisconnectPeripheral is handled in class BluetoothTransmitter, however a delegate may be interested that a disconnect occurred for showing info to the user. The implementation of centralManager didDisconnectPeripheral in BluetoothTransmitter class would first do own stuff (eg try to reconnect) and at the end call the corresponding delegate function. For some methods, the implementation in the class BluetoothTransmitter will do nothing but calling the corresponding method in the protocol BluetoothTransmitterDelegate, example centralManager didUpdateValueFor characteristic.
///
/// If a Delegate is not interested in information, then it just needs to implement an empty closure
///
/// Most delegates will only implement just a very fiew of the methods
protocol BluetoothTransmitterDelegate:AnyObject {
/// called when centralManager didConnect was called in BlueToothTransmitter class
/// the BlueToothTransmitter class handles the reconnect but the delegate class can for instance show the connection status to the user
/// delegate used for any type of BluetoothTransmitter
protocol BluetoothTransmitterDelegate: AnyObject {
// MARK: - Generic functions that can be used for any type of BluetoothTransmitter
/// did connect to
/// - parameters:
/// - address: the address that was received from the transmitter during connection phase
/// - name: the name that was received from the transmitter during connection phase
func centralManagerDidConnect(address:String?, name:String?)
/// - bluetoothTransmitter : the bluetoothTransmitter to which the connection is made
func didConnectTo(bluetoothTransmitter: BluetoothTransmitter)
/// called when centralManager didFailToConnect was called in BlueToothTransmitter class
/// the BlueToothTransmitter class handles will try to reconnect but the delegate class can for instance show the connection status to the user
func centralManagerDidFailToConnect(error: Error?)
/// did disconnect from bluetoothTransmitter
func didDisconnectFrom(bluetoothTransmitter: BluetoothTransmitter)
/// called when centralManagerDidUpdateState was called in BlueToothTransmitter class
/// if an address is already stored (ie device already connected before) then the BlueToothTransmitter class will try to reconnect and/or scan
func centralManagerDidUpdateState(state: CBManagerState)
/// the ios device did change bluetooth status
func deviceDidUpdateBluetoothState(state: CBManagerState, bluetoothTransmitter: BluetoothTransmitter)
/// if an address is already stored (ie device already connected before) then the BlueToothTransmitter class will try to reconnect and/or scan
///
/// the BlueToothTransmitter will also log the error if there is one
func centralManagerDidDisconnectPeripheral(error: Error?)
/// called when peripheral didUpdateNotificationStateFor was called in BlueToothTransmitter class
///
/// the BlueToothTransmitter class will log the error if any and call the delegate function
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?)
/// called when peripheral didUpdateValueFor was called in BlueToothTransmitter class
/// the BlueToothTransmitter class will not do anything just call the delegate function
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?)
}

View File

@ -0,0 +1,52 @@
import Foundation
extension M5Stack: BluetoothPeripheral {
func getDeviceName() -> String {
return name
}
func setAlias(_ value: String?) {
alias = value
}
func getAlias() -> String? {
return alias
}
// get the mac address
func getAddress() -> String {
return address
}
// get the type of BluetoothPeripheral: "M5Strack", ...
func bluetoothPeripheralType() -> BluetoothPeripheralType {
return .M5Stack
}
func dontTryToConnectToThisBluetoothPeripheral() {
shouldconnect = false
}
func alwaysTryToConnectToThisBluetoothPeripheral() {
shouldconnect = true
}
func shouldXdripTryToConnectToThisBluetoothPeripheral() -> Bool {
return shouldconnect
}
func parameterUpdateNotNeededAtNextConnect() {
parameterUpdateNeeded = false
}
func parameterUpdateNeededAtNextConnect() {
parameterUpdateNeeded = true
}
func isParameterUpdateNeededAtNextConnect() -> Bool {
return parameterUpdateNeeded
}
}

View File

@ -1,40 +0,0 @@
import Foundation
import CoreBluetooth
protocol M5StackBluetoothDelegate: AnyObject {
/// if a new ble password is received from M5Stack
func newBlePassWord(newBlePassword: String, forM5Stack m5Stack:M5Stack)
/// did the app successfully authenticate towards M5Stack
///
/// in case of failure, then user should set the correct password in the M5Stack ini file, or, in case there's no password set in the ini file, switch off and on the M5Stack
func authentication(success: Bool, forM5Stack m5Stack:M5Stack)
/// there's no ble password set during M5Stack init, user should set it in the settings
func blePasswordMissing(forM5Stack m5Stack:M5Stack)
/// it's an M5Stack without password configired in the ini file. xdrip app has been requesting temp password to M5Stack but this was already done once. M5Stack needs to be reset
func m5StackResetRequired(forM5Stack m5Stack:M5Stack)
/// did connect to M5Stack
/// - parameters:
/// - forM5Stack : if nil then it's the first connection to a new M5Stack, time to create a new M5Stack instance with address and string which should now not be nil
/// - bluetoothTransmitter : is needed in case we want to disconnect from it
func didConnect(forM5Stack m5Stack: M5Stack?, address: String?, name: String?, bluetoothTransmitter : M5StackBluetoothTransmitter)
/// did disconnect from M5Stack
func didDisconnect(forM5Stack m5Stack:M5Stack)
/// the ios device did change bluetooth status
func deviceDidUpdateBluetoothState(state: CBManagerState, forM5Stack m5Stack:M5Stack)
/// to pass some text error message, delegate can decide to show to user, log, ...
func error(message: String)
/// will be called if M5Stack is connected, and authentication was successful
func isReadyToReceiveData(m5Stack : M5Stack)
func isAskingForAllParameters(m5Stack: M5Stack)
}

View File

@ -2,18 +2,13 @@ import Foundation
import CoreBluetooth
import os
/// bluetoothTransmitter for an M5 Stack
/// bluetoothTransmitter for an M5Stack
/// - will start scanning as soon as created or as soon as bluetooth is switched on
/// - there's only one characteristic which is used for write and notify, not read. To get data from the M5Stack, xdrip app will write a specific opcode, then M5Stack will reply and the reply will arrive as notify. So we don't use .withResponse
final class M5StackBluetoothTransmitter: BluetoothTransmitter, BluetoothTransmitterDelegate {
final class M5StackBluetoothTransmitter: BluetoothTransmitter {
// MARK: - public properties
/// will be used to pass data back
///
/// there's two delegates, one public, one private. The private one will be assigned during object creation. There's two because two other classes want to receive feedback : BluetoothPeripheralManager and UIViewControllers. There's only one instance of BluetoothPeripheralManager and it's always the same. An instance will be assigned to m5StackBluetoothTransmitterDelegateFixed. There can be different UIViewController' and they can change, an instance will be assigned to m5StackBluetoothTransmitterDelegateVariable
public weak var m5StackBluetoothTransmitterDelegateVariable: M5StackBluetoothDelegate?
// MARK: - private properties
/// service to be discovered
@ -31,56 +26,34 @@ final class M5StackBluetoothTransmitter: BluetoothTransmitter, BluetoothTransmit
/// temporary storage for packages received from M5Stack, with the password in it
private var blePasswordM5StackPacket = M5StackPacket()
/// m5Stack instance for which this bluetooth transmitter is working, if nil then this bluetoothTransmitter is created to start scanning for a new, unknown M5Stack. In that case, the value needs to be assigned as soon as an M5Stack is created
public var m5Stack: M5Stack?
/// will be used to pass data back
///
/// there's two delegates, one public, one private. The private one will be assigned during object creation. There's two because two other classes want to receive feedback : BluetoothPeripheralManager and UIViewControllers. There's only one instance of BluetoothPeripheralManager and it's always the same. An instance will be assigned to m5StackBluetoothTransmitterDelegateFixed. There can be different UIViewController' and they can change, an instance will be assigned to m5StackBluetoothTransmitterDelegateVariable
private weak var m5StackBluetoothTransmitterDelegateFixed:M5StackBluetoothDelegate!
/// is the transmitter ready to receive data like parameter updates or reading values
private (set) var isReadyToReceiveData: Bool = false
/// possible rotation values, , the value is how it will be sent to the M5Stack but not how it's stored in the M5Stack object - In the M5Stack object we store an Int value which is used as index in rotationValues and rotationStrings
private let rotationValues: [UInt16] = [ 1, 2, 3, 0]
// MARK: - initializer
/// - parameters:
/// - m5Stack : an instance of M5Stack, if nil then this instance is created to scan for a new M5Stack, address and name not yet known, so not possible yet to create an M5Stack instance
/// - delegateFixed : there's two delegates, one public, one private. The private one will be assigned during object creation. There's two because two other classes want to receive feedback : BluetoothPeripheralManager and UIViewControllers. There's only one instance of BluetoothPeripheralManager and it's always the same. An instance will be assigned to m5StackBluetoothTransmitterDelegateFixed. There can be different UIViewController' and they can change, an instance will be assigned to m5StackBluetoothTransmitterDelegateVariable
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
/// - delegate : the M5StackBluetoothTransmitterDelegate
/// - blePassword : optional. If nil then xdrip will send a M5StackReadBlePassWordTxMessage to the M5Stack, so this would be a case where the M5Stack (all M5Stacks managed by xdrip) do not have a fixed blepassword
/// The blepassword in the M5Stack object gets priority over the blePassword in the parameter list
init(m5Stack: M5Stack?, delegateFixed: M5StackBluetoothDelegate, blePassword: String?) {
init(address:String?, name: String?, delegate: M5StackBluetoothTransmitterDelegate, blePassword: String?) {
// assign addressname and name or expected devicename
// assign addressname and name, assume it's not been connected before
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "M5Stack")
if let m5Stack = m5Stack {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: m5Stack.address, name: m5Stack.name)
// if address not nil, then we've already connected before to this device, we know the address and name
if let address = address, let name = name {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// as mentioned in the parameter documentation, the blePassword in the M5Stack parameter gets priority over the password in the parameter blePassword
if let m5Stack = m5Stack, let blePassword = m5Stack.blepassword {
// m5Stack object is not nil and does have a blePassword, use that value
self.blePassword = blePassword
} else {
// use blePassword that was in the parameter list, possibly nil value
self.blePassword = blePassword
}
self.m5Stack = m5Stack
// assign blePassword
self.blePassword = blePassword
self.m5StackBluetoothTransmitterDelegateFixed = delegateFixed
// call super
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service)], CBUUID_ReceiveCharacteristic: CBUUID_TxRxCharacteristic, CBUUID_WriteCharacteristic: CBUUID_TxRxCharacteristic, startScanningAfterInit: false)
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
// start scanning, probably the scanning will not yet start, this will happen only when centralManagerDidUpdateState is called
_ = startScanning()
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Service)], CBUUID_ReceiveCharacteristic: CBUUID_TxRxCharacteristic, CBUUID_WriteCharacteristic: CBUUID_TxRxCharacteristic, startScanningAfterInit: false, bluetoothTransmitterDelegate: delegate)
}
@ -266,51 +239,38 @@ final class M5StackBluetoothTransmitter: BluetoothTransmitter, BluetoothTransmit
} else {return false}
}
// MARK: - BluetoothTransmitterDelegate functions
// MARK: - overriden BluetoothTransmitter functions
func centralManagerDidConnect(address: String?, name: String?) {
m5StackBluetoothTransmitterDelegateFixed.didConnect(forM5Stack: m5Stack, address: address, name: name, bluetoothTransmitter: self)
m5StackBluetoothTransmitterDelegateVariable?.didConnect(forM5Stack: m5Stack, address: address, name: name, bluetoothTransmitter: self)
}
func centralManagerDidFailToConnect(error: Error?) {
trace("in centralManagerDidFailToConnect", log: log, type: .error)
}
func centralManagerDidUpdateState(state: CBManagerState) {
override func centralManagerDidUpdateState(_ central: CBCentralManager) {
if let m5Stack = m5Stack {
m5StackBluetoothTransmitterDelegateFixed!.deviceDidUpdateBluetoothState(state: state, forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.deviceDidUpdateBluetoothState(state: state, forM5Stack: m5Stack)
} else {
super.centralManagerDidUpdateState(central)
if deviceAddress == nil {
/// this bluetoothTransmitter is created to start scanning for a new, unknown M5Stack, so start scanning
_ = startScanning()
}
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
override func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
super.centralManager(central, didDisconnectPeripheral: peripheral, error: error)
// can not write data anymore to the device
isReadyToReceiveData = false
if let m5Stack = m5Stack {
m5StackBluetoothTransmitterDelegateFixed!.didDisconnect(forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.didDisconnect(forM5Stack: m5Stack)
}
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateNotificationStateFor: characteristic, error: error)
// check if subscribe to notifications succeeded
if characteristic.isNotifying {
// time to send password to M5Stack, if there isn't one known in the settings then we assume that the user didn't set a password in the M5Stack config (M5NS.INI) and that we didn't connect to this M5Stack yet after the last M5Stack restart. So in that case we will request a random password
if let blePassword = blePassword, let authenticateTxMessageData = M5StackAuthenticateTXMessage(password: blePassword).data {
if !writeDataToPeripheral(data: authenticateTxMessageData, type: .withoutResponse) {
trace("failed to send authenticateTx to M5Stack", log: log, type: .error)
} else {
@ -324,16 +284,19 @@ final class M5StackBluetoothTransmitter: BluetoothTransmitter, BluetoothTransmit
} else {
trace("successfully sent readBlePassWordTx to M5Stack", log: log, type: .error)
}
}
} else {
trace("in peripheralDidUpdateNotificationStateFor, failed to subscribe for characteristic %{public}@", log: log, type: .error, characteristic.uuid.description)
trace("in peripheralDidUpdateNotificationStateFor, failed to subscribe for characteristic %{public}@", log: log, type: .error, characteristic.uuid.description)
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
//check if value is not nil
guard let value = characteristic.value else {
trace("in peripheral didUpdateValueFor, characteristic.value is nil", log: log, type: .info)
@ -363,82 +326,74 @@ final class M5StackBluetoothTransmitter: BluetoothTransmitter, BluetoothTransmit
trace(" received new password, will store it", log: log, type: .error)
guard let m5Stack = m5Stack else {return}// would be a software error if this happens
blePasswordM5StackPacket.addNewPacket(value: value)
if let newBlePassword = blePasswordM5StackPacket.getText() {
self.blePassword = newBlePassword
m5StackBluetoothTransmitterDelegateFixed!.newBlePassWord(newBlePassword: newBlePassword, forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.newBlePassWord(newBlePassword: newBlePassword, forM5Stack: m5Stack)
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)? .newBlePassWord(newBlePassword: newBlePassword, m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.newBlePassWord(newBlePassword: newBlePassword, m5StackBluetoothTransmitter: self)
}
// this is usually a case where M5Stack has restarted, so it needs to get time and timeoffset
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
// this is the time when the M5stack is ready to receive readings or parameter updates
isReadyToReceiveData = true
m5StackBluetoothTransmitterDelegateFixed!.isReadyToReceiveData(m5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.isReadyToReceiveData(m5Stack: m5Stack)
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.isReadyToReceiveData(m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.isReadyToReceiveData(m5StackBluetoothTransmitter: self)
case .authenticateSuccessRx:
// received from M5Stack, need to inform delegates, send timestamp to M5Stack, and also set isReadyToReceiveData to true
trace(" successfully authenticated", log: log, type: .error)
guard let m5Stack = m5Stack else {return}// would be a software error if this happens
// inform delegates
m5StackBluetoothTransmitterDelegateFixed!.authentication(success: true, forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.authentication(success: true, forM5Stack: m5Stack)
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.authentication(success: true, m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.authentication(success: true, m5StackBluetoothTransmitter: self)
// even though not requested, and even if M5Stack may already have it, send the local time
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
// this is the time when the M5stack is ready to receive readings or parameter updates
isReadyToReceiveData = true
m5StackBluetoothTransmitterDelegateFixed!.isReadyToReceiveData(m5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.isReadyToReceiveData(m5Stack: m5Stack)
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.isReadyToReceiveData(m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.isReadyToReceiveData(m5StackBluetoothTransmitter: self)
case .authenticateFailureRx:
// received authentication failure, inform delegates
if let m5Stack = m5Stack {
m5StackBluetoothTransmitterDelegateFixed!.authentication(success: false, forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.authentication(success: false, forM5Stack: m5Stack)
}
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.authentication(success: false, m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.authentication(success: false, m5StackBluetoothTransmitter: self)
case .readBlePassWordError1Rx:
if let m5Stack = m5Stack {
m5StackBluetoothTransmitterDelegateFixed!.blePasswordMissing(forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.blePasswordMissing(forM5Stack: m5Stack)
}
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.blePasswordMissing(m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.blePasswordMissing(m5StackBluetoothTransmitter: self)
case .readBlePassWordError2Rx:
if let m5Stack = m5Stack {
m5StackBluetoothTransmitterDelegateFixed!.m5StackResetRequired(forM5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.m5StackResetRequired(forM5Stack: m5Stack)
}
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.m5StackResetRequired(m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.m5StackResetRequired(m5StackBluetoothTransmitter: self)
case .readTimeStampRx:
// M5Stack is requesting for timestamp
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
case .readAllParametersRx:
// M5Stack is asking for all parameters
guard let m5Stack = m5Stack else {return}// would be a software error if this happens
m5StackBluetoothTransmitterDelegateFixed!.isAskingForAllParameters(m5Stack: m5Stack)
m5StackBluetoothTransmitterDelegateVariable?.isAskingForAllParameters(m5Stack: m5Stack)
(fixedBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.isAskingForAllParameters(m5StackBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? M5StackBluetoothTransmitterDelegate)?.isAskingForAllParameters(m5StackBluetoothTransmitter: self)
}
}
}
// MARK: private helper functions
/// sends local time in seconds since 1.1.1970 and also timeoffset from UTC in seconds. to M5Stack

View File

@ -0,0 +1,25 @@
import Foundation
protocol M5StackBluetoothTransmitterDelegate: BluetoothTransmitterDelegate {
/// will be called if M5StackBluetoothTransmitter is connected and ready to receive data
func isReadyToReceiveData(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter)
/// M5StackBluetoothTransmitter wants to receive all parameters
func isAskingForAllParameters(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter)
/// if a new ble password is received from M5StackBluetoothTransmitter
func newBlePassWord(newBlePassword: String, m5StackBluetoothTransmitter: M5StackBluetoothTransmitter)
/// did the app successfully authenticate towards M5Stack
///
/// in case of failure, then user should set the correct password in the M5Stack ini file, or, in case there's no password set in the ini file, switch off and on the M5Stack
func authentication(success: Bool, m5StackBluetoothTransmitter: M5StackBluetoothTransmitter)
/// there's no ble password set during M5Stack init, user should set it in the settings
func blePasswordMissing(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter)
/// it's an M5Stack without password configired in the ini file. xdrip app has been requesting temp password to M5Stack but this was already done once. M5Stack needs to be reset
func m5StackResetRequired(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter)
}

View File

@ -8,7 +8,7 @@ final class BluetoothPeripheralNavigationController: UINavigationController {
private var coreDataManager:CoreDataManager?
/// a bluetoothPeripheralManager
private weak var bluetoothPeripheralManager: BluetoothPeripheralManaging?
private weak var bluetoothPeripheralManager: BluetoothPeripheralManaging!
// MARK:- public functions
@ -34,7 +34,7 @@ final class BluetoothPeripheralNavigationController: UINavigationController {
extension BluetoothPeripheralNavigationController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if let bluetoothPeripheralsViewController = viewController as? BluetoothPeripheralsViewController, let coreDataManager = coreDataManager, let bluetoothPeripheralManager = bluetoothPeripheralManager {
if let bluetoothPeripheralsViewController = viewController as? BluetoothPeripheralsViewController, let coreDataManager = coreDataManager {
bluetoothPeripheralsViewController.configure(coreDataManager: coreDataManager, bluetoothPeripheralManager: bluetoothPeripheralManager)
}
}

View File

@ -0,0 +1,39 @@
import Foundation
import UIKit
protocol BluetoothPeripheralViewModel: BluetoothTransmitterDelegate {
/// to be called before opening the actual viewcontroller or after discovering a new bluetoothperipheral
/// - parameters :
/// - bluetoothTransmitterDelegate : usually the uiViewController
/// - bluetoothPeripheral : if nil then the viewcontroller is opened to scan for a new peripheral
/// - bluetoothPeripheralManager : reference to bluetoothPeripheralManaging object
/// - tableView : needed to intiate refresh of row
/// - bluetoothPeripheralViewController : BluetoothPeripheralViewController
/// - settingRowOffset :needs to be set to number of generic settings in BluetoothPeripheralViewController
func configure(bluetoothPeripheral: BluetoothPeripheral?, bluetoothPeripheralManager: BluetoothPeripheralManaging, tableView: UITableView, bluetoothPeripheralViewController: BluetoothPeripheralViewController, settingRowOffset:Int, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate)
/// screen title for uiviewcontroller
func screenTitle() -> String
/// user clicked done button in uiviewcontroller
/// - parameters:
/// - bluetoothPeripheral : the bluetoothPeripheral being shown
func doneButtonHandler(bluetoothPeripheral: BluetoothPeripheral?)
/// updates the contents of a cell, for setting with rawValue withSettingRawValue
func update(cell: UITableViewCell, withSettingRawValue rawValue: Int, for bluetoothPeripheral: BluetoothPeripheral)
/// user clicked a row, this function does the necessary
func userDidSelectRow(withSettingRawValue rawValue: Int, for bluetoothPeripheral: BluetoothPeripheral, bluetoothPeripheralManager: BluetoothPeripheralManaging, doneButtonOutlet: UIBarButtonItem)
/// get number of settings in the viewmodel
func numberOfSettings() -> Int
/// used when new peripheral is discovered and connected, to temporary store values in model (eg in case of M5Stack, store the rotation value which would be a default value)
func storeTempValues(from bluetoothPeripheral: BluetoothPeripheral)
/// used when user clicks done button in uiviewcontroller
func writeTempValues(to bluetoothPeripheral: BluetoothPeripheral)
}

View File

@ -0,0 +1,542 @@
import Foundation
import UIKit
import CoreBluetooth
/// - a case per attribute that can be set
/// - these are attributes specific to M5Stack, the generic ones are defined in BluetoothPeripheralViewController
fileprivate enum Setting:Int, CaseIterable {
/// ble password
case blePassword = 0
/// textColor
case textColor = 1
/// backGroundColor
case backGroundColor = 2
/// rotation
case rotation = 3
/// case brightness
case brightness = 4
}
class M5StackBluetoothPeripheralViewModel {
// MARK: - private properties
/// textColor to be used in M5Stack
///
/// temp storage of value while user is editing the M5Stack attributes
private var textColorTemporaryValue: M5StackColor?
/// roration to be used in M5Stack
///
/// temp storage of value while user is editing the M5Stack attributes
private var rotationTempValue: UInt16?
/// backGroundColor to be used in M5Stack
///
/// temp storage of value while user is editing the M5Stack attributes
private var backGroundColorTemporaryValue: M5StackColor?
/// brightness to be used in M5Stack
///
/// temp storage of value while user is editing the M5Stack attributes
private var brightnessTemporaryValue: Int?
/// possible rotation keys, , the value is shown to the user
private let rotationStrings: [String] = [ "0", "90", "180", "270"]
/// possible brightness values, the value is shown to the user
private let brightnessStrings: [String] = ["0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"]
/// reference to bluetoothPeripheralManager
private weak var bluetoothPeripheralManager: BluetoothPeripheralManaging?
/// reference to the tableView
private weak var tableView: UITableView?
/// reference to BluetoothPeripheralViewController that will own this M5StackBluetoothPeripheralViewModel - needed to present stuff etc
private weak var bluetoothPeripheralViewController: BluetoothPeripheralViewController?
private weak var bluetoothTransmitterDelegate: BluetoothTransmitterDelegate?
/// this is actually the number of setting rows defined in BluetoothPeripheralViewController
private var settingRowOffset: Int!
}
// MARK: - extension BluetoothTransmitterDelegate
extension M5StackBluetoothPeripheralViewModel: BluetoothTransmitterDelegate {
func didConnectTo(bluetoothTransmitter: BluetoothTransmitter) {
bluetoothTransmitterDelegate?.didConnectTo(bluetoothTransmitter: bluetoothTransmitter)
}
func didDisconnectFrom(bluetoothTransmitter: BluetoothTransmitter) {
bluetoothTransmitterDelegate?.didDisconnectFrom(bluetoothTransmitter: bluetoothTransmitter)
}
func deviceDidUpdateBluetoothState(state: CBManagerState, bluetoothTransmitter: BluetoothTransmitter) {
bluetoothTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state, bluetoothTransmitter: bluetoothTransmitter)
}
}
// MARK: - extension M5StackBluetoothTransmitterDelegate
extension M5StackBluetoothPeripheralViewModel: M5StackBluetoothTransmitterDelegate {
func isAskingForAllParameters(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
// viewcontroller doesn't use this
}
func isReadyToReceiveData(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
// viewcontroller doesn't use this
}
func newBlePassWord(newBlePassword: String, m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
// note : blePassword is also saved in BluetoothPeripheralManager, it will be saved two times
if let m5StackPeripheral = bluetoothPeripheralManager?.getBluetoothPeripheral(for: m5StackBluetoothTransmitter) as? M5Stack {
m5StackPeripheral.blepassword = newBlePassword
tableView?.reloadRows(at: [IndexPath(row: Setting.blePassword.rawValue + settingRowOffset, section: 0)], with: .none)
}
}
func authentication(success: Bool, m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
if !success, let m5StackPeripheral = bluetoothPeripheralManager?.getBluetoothPeripheral(for: m5StackBluetoothTransmitter) as? M5Stack {
// show warning, inform that user should set password or reset M5Stack
let alert = UIAlertController(title: Texts_Common.warning, message: Texts_M5StackView.authenticationFailureWarning + " " + Text_BluetoothPeripheralView.alwaysConnect, actionHandler: {
// by the time user clicks 'ok', the M5stack will be disconnected by the BluetoothPeripheralManager (see authentication in BluetoothPeripheralManager)
self.bluetoothPeripheralViewController?.setShouldConnectToFalse(for: m5StackPeripheral)
})
bluetoothPeripheralViewController?.present(alert, animated: true, completion: nil)
}
}
func blePasswordMissing(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
guard let m5StackPeripheral = bluetoothPeripheralManager?.getBluetoothPeripheral(for: m5StackBluetoothTransmitter) as? M5Stack else {return}
// show warning, inform that user should set password
let alert = UIAlertController(title: Texts_Common.warning, message: Texts_M5StackView.authenticationFailureWarning + " " + Text_BluetoothPeripheralView.alwaysConnect, actionHandler: {
// by the time user clicks 'ok', the M5stack will be disconnected by the BluetoothPeripheralManager (see authentication in BluetoothPeripheralManager)
self.bluetoothPeripheralViewController?.setShouldConnectToFalse(for: m5StackPeripheral)
})
bluetoothPeripheralViewController?.present(alert, animated: true, completion: nil)
}
func m5StackResetRequired(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
guard let m5StackBluetoothPeripheral = bluetoothPeripheralManager?.getBluetoothPeripheral(for: m5StackBluetoothTransmitter) as? M5Stack else {return}
// show warning, inform that user should reset M5Stack
let alert = UIAlertController(title: Texts_Common.warning, message: Texts_M5StackView.m5StackResetRequiredWarning + " " + Text_BluetoothPeripheralView.alwaysConnect, actionHandler: {
// by the time user clicks 'ok', the M5stack will be disconnected by the BluetoothPeripheralManager (see authentication in BluetoothPeripheralManager)
self.bluetoothPeripheralViewController?.setShouldConnectToFalse(for: m5StackBluetoothPeripheral)
})
bluetoothPeripheralViewController?.present(alert, animated: true, completion: nil)
}
}
// MARK: - extension BluetoothPeripheralViewModelProtocol
extension M5StackBluetoothPeripheralViewModel: BluetoothPeripheralViewModel {
func writeTempValues(to bluetoothPeripheral: BluetoothPeripheral) {
guard let m5StackBluetoothPeripheral = bluetoothPeripheral as? M5Stack else {return}
if let textColorTemporaryValue = textColorTemporaryValue {
m5StackBluetoothPeripheral.textcolor = Int32(textColorTemporaryValue.rawValue)
}
if let rotation = rotationTempValue {
m5StackBluetoothPeripheral.rotation = Int32(rotation)
}
if let backGroundColor = backGroundColorTemporaryValue {
m5StackBluetoothPeripheral.backGroundColor = Int32(backGroundColor.rawValue)
}
if let brightness = brightnessTemporaryValue {
m5StackBluetoothPeripheral.brightness = Int16(brightness)
}
}
func storeTempValues(from bluetoothPeripheral: BluetoothPeripheral) {
guard let m5StackBluetoothPeripheral = bluetoothPeripheral as? M5Stack else {return}
// temporary store the value of textColor, user can change this via the view, it will be stored back in the m5StackASNSObject only after clicking 'done' button
textColorTemporaryValue = M5StackColor(forUInt16: UInt16(m5StackBluetoothPeripheral.textcolor))
// temporary store the value of rotation, user can change this via the view, it will be stored back in the m5StackASNSObject only after clicking 'done' button
rotationTempValue = UInt16(m5StackBluetoothPeripheral.rotation)
// temporary store the value of backGroundColor, user can change this via the view, it will be stored back in the m5StackASNSObject only after clicking 'done' button
backGroundColorTemporaryValue = M5StackColor(forUInt16: UInt16(m5StackBluetoothPeripheral.backGroundColor))
// temporary store the value of brightness, user can change this via the view, it will be stored back in the m5StackASNSObject only after clicking 'done' button
brightnessTemporaryValue = Int(m5StackBluetoothPeripheral.brightness)
}
func numberOfSettings() -> Int {
return Setting.allCases.count
}
func userDidSelectRow(withSettingRawValue rawValue: Int, for bluetoothPeripheral: BluetoothPeripheral, bluetoothPeripheralManager: BluetoothPeripheralManaging, doneButtonOutlet: UIBarButtonItem) {
guard let setting = Setting(rawValue: rawValue) else { fatalError("M5StackBluetoothPeripheralViewModel userDidSelectRow, unexpected setting") }
switch setting {
case .blePassword:
break
case .textColor:
var texts = [String]()
var colors = [M5StackColor]()
for textColor in M5StackColor.allCases {
texts.append(textColor.description)
colors.append(textColor)
}
//find index for color stored in M5Stack or userdefaults
var selectedRow:Int?
if let textColor = textColorTemporaryValue {
selectedRow = texts.firstIndex(of:textColor.description)
} else if let textColor = UserDefaults.standard.m5StackTextColor?.description {
selectedRow = texts.firstIndex(of:textColor)
}
// configure PickerViewData
let pickerViewData = PickerViewData(withMainTitle: nil, withSubTitle: Texts_SettingsView.m5StackTextColor, withData: texts, selectedRow: selectedRow, withPriority: nil, actionButtonText: nil, cancelButtonText: nil, onActionClick: {(_ index: Int) in
if index != selectedRow {
// set temp value textColor to new textColor
self.textColorTemporaryValue = colors[index]
// send value to M5Stack, if that would fail then set updateNeeded for that m5Stack
if let m5StackPeripheral = bluetoothPeripheral as? M5Stack {
if let textColor = self.textColorTemporaryValue, let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: m5StackPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = blueToothTransmitter as? M5StackBluetoothTransmitter, m5StackBluetoothTransmitter.writeTextColor(textColor: textColor) {
// do nothing, textColor successfully written to m5Stack - although it's not yet 100% sure because
} else {
m5StackPeripheral.parameterUpdateNeededAtNextConnect()
}
}
// reload table
self.tableView?.reloadRows(at: [IndexPath(row: Setting.textColor.rawValue + self.settingRowOffset, section: 0)], with: .none)
// enable the done button
doneButtonOutlet.enable()
}
}, onCancelClick: nil, didSelectRowHandler: nil)
// create and present PickerViewController
if let bluetoothPeripheralViewController = bluetoothPeripheralViewController {
PickerViewController.displayPickerViewController(pickerViewData: pickerViewData, parentController: bluetoothPeripheralViewController)
}
case .backGroundColor:
var texts = [String]()
var colors = [M5StackColor]()
for backGroundColor in M5StackColor.allCases {
texts.append(backGroundColor.description)
colors.append(backGroundColor)
}
//find index for color stored in M5Stack or userdefaults
var selectedRow:Int?
if let backGroundColor = backGroundColorTemporaryValue {
// backGroundColor is an instance of textColor, description gives us the textual representation
selectedRow = texts.firstIndex(of:backGroundColor.description)
} else {
selectedRow = texts.firstIndex(of:ConstantsM5Stack.defaultBackGroundColor.description)
}
// configure PickerViewData
let pickerViewData = PickerViewData(withMainTitle: nil, withSubTitle: Texts_SettingsView.m5StackbackGroundColor, withData: texts, selectedRow: selectedRow, withPriority: nil, actionButtonText: nil, cancelButtonText: nil, onActionClick: {(_ index: Int) in
if index != selectedRow {
// set temp value backGroundColor to new backGroundColor
self.backGroundColorTemporaryValue = colors[index]
// send value to M5Stack, if that would fail then set updateNeeded for that m5Stack
if let m5StackPeripheral = bluetoothPeripheral as? M5Stack {
if let backGroundColor = self.backGroundColorTemporaryValue, let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: m5StackPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = blueToothTransmitter as? M5StackBluetoothTransmitter, m5StackBluetoothTransmitter.writeBackGroundColor(backGroundColor: backGroundColor) {
// do nothing, backGroundColor successfully written to m5Stack - although it's not yet 100% sure because write returns true without waiting for response from bluetooth peripheral
} else {
m5StackPeripheral.parameterUpdateNeededAtNextConnect()
}
}
// reload table
self.tableView?.reloadRows(at: [IndexPath(row: Setting.backGroundColor.rawValue + self.settingRowOffset, section: 0)], with: .none)
// enable the done button
doneButtonOutlet.enable()
}
}, onCancelClick: nil, didSelectRowHandler: nil)
// create and present PickerViewController
if let bluetoothPeripheralViewController = bluetoothPeripheralViewController {
PickerViewController.displayPickerViewController(pickerViewData: pickerViewData, parentController: bluetoothPeripheralViewController)
}
case .rotation:
//find index for rotation stored in M5Stack or userdefaults
var selectedRow:Int? = nil
if let rotation = rotationTempValue {
selectedRow = Int(rotation)
} else {
selectedRow = Int(ConstantsM5Stack.defaultRotation)
}
// configure PickerViewData
let pickerViewData = PickerViewData(withMainTitle: nil, withSubTitle: Texts_SettingsView.m5StackRotation, withData: rotationStrings, selectedRow: selectedRow, withPriority: nil, actionButtonText: nil, cancelButtonText: nil, onActionClick: {(_ index: Int) in
if index != selectedRow {
// set rotationTempValue to new rotation
self.rotationTempValue = UInt16(index)
// send value to M5Stack, if that would fail then set updateNeeded for that m5Stack
if let m5StackAsPeripheral = bluetoothPeripheral as? M5Stack {
if let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: m5StackAsPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = blueToothTransmitter as? M5StackBluetoothTransmitter, m5StackBluetoothTransmitter.writeRotation(rotation: index) {
// do nothing, rotation successfully written to m5Stack - although it's not yet 100% sure because write returns true without waiting for response from bluetooth peripheral
} else {
m5StackAsPeripheral.parameterUpdateNeededAtNextConnect()
}
}
// reload table
self.tableView?.reloadRows(at: [IndexPath(row: Setting.rotation.rawValue + self.settingRowOffset, section: 0)], with: .none)
// enable the done button
doneButtonOutlet.enable()
}
}, onCancelClick: nil, didSelectRowHandler: nil)
// create and present PickerViewController
if let bluetoothPeripheralViewController = bluetoothPeripheralViewController {
PickerViewController.displayPickerViewController(pickerViewData: pickerViewData, parentController: bluetoothPeripheralViewController)
}
case .brightness:
//find index for brightness stored in M5Stack or use 100 as default value
var selectedRow:Int? = nil
// brightness goes from 0 to 100, in steps of 10. Dividing by 10 gives the selected row
if let brightness = brightnessTemporaryValue {
selectedRow = brightness/10
} else {
// default value is 100
selectedRow = 10
}
// configure PickerViewData
let pickerViewData = PickerViewData(withMainTitle: nil, withSubTitle: Texts_SettingsView.m5StackBrightness, withData: brightnessStrings, selectedRow: selectedRow, withPriority: nil, actionButtonText: nil, cancelButtonText: nil, onActionClick: {(_ index: Int) in
if index != selectedRow {
// set rotationTempValue to new rotation
self.brightnessTemporaryValue = index * 10
// send value to M5Stack, if that would fail then set updateNeeded for that m5Stack
if let m5StackPeripheral = bluetoothPeripheral as? M5Stack {
if let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: m5StackPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = blueToothTransmitter as? M5StackBluetoothTransmitter, m5StackBluetoothTransmitter.writeBrightness(brightness: index * 10) {
// do nothing, brightness successfully written to m5Stack - although it's not yet 100% sure because write returns true without waiting for response from bluetooth peripheral
} else {
m5StackPeripheral.parameterUpdateNeededAtNextConnect()
}
}
// reload table
self.tableView?.reloadRows(at: [IndexPath(row: Setting.brightness.rawValue + self.settingRowOffset, section: 0)], with: .none)
// enable the done button
doneButtonOutlet.enable()
}
}, onCancelClick: nil, didSelectRowHandler: nil)
// create and present PickerViewController
if let bluetoothPeripheralViewController = bluetoothPeripheralViewController {
PickerViewController.displayPickerViewController(pickerViewData: pickerViewData, parentController: bluetoothPeripheralViewController)
}
}
}
func update(cell: UITableViewCell, withSettingRawValue rawValue: Int, for bluetoothPeripheral: BluetoothPeripheral) {
// verify that rawValue is within range of setting
guard let setting = Setting(rawValue: rawValue) else { fatalError("M5StackBluetoothPeripheralViewModel update, Unexpected setting")
}
// verify that bluetoothPeripheralAsNSObject is an M5Stack
guard let m5Stack = bluetoothPeripheral as? M5Stack else {
fatalError("M5StackBluetoothPeripheralViewModel update, bluetoothPeripheral is not M5Stack")
}
// default value for accessoryView is nil
cell.accessoryView = nil
// configure the cell depending on setting
switch setting {
case .blePassword:
cell.textLabel?.text = Texts_Common.password
cell.detailTextLabel?.text = m5Stack.blepassword
cell.accessoryType = .none
case .textColor:
cell.textLabel?.text = Texts_SettingsView.m5StackTextColor
if let textColor = textColorTemporaryValue {
cell.detailTextLabel?.text = textColor.description
} else {
if let textColor = UserDefaults.standard.m5StackTextColor {
cell.detailTextLabel?.text = textColor.description
} else {
cell.detailTextLabel?.text = ConstantsM5Stack.defaultTextColor.description
}
}
cell.accessoryType = .disclosureIndicator
case .backGroundColor:
cell.textLabel?.text = Texts_SettingsView.m5StackbackGroundColor
if let backGroundColor = backGroundColorTemporaryValue {
cell.detailTextLabel?.text = backGroundColor.description
} else {
cell.detailTextLabel?.text = ConstantsM5Stack.defaultBackGroundColor.description
}
cell.accessoryType = .disclosureIndicator
case .rotation:
cell.textLabel?.text = Texts_SettingsView.m5StackRotation
if let rotation = rotationTempValue {
cell.detailTextLabel?.text = rotationStrings[Int(rotation)]
} else {
cell.detailTextLabel?.text = rotationStrings[Int(ConstantsM5Stack.defaultRotation)]
}
cell.accessoryType = .disclosureIndicator
case .brightness:
cell.textLabel?.text = Texts_SettingsView.m5StackBrightness
if let brightness = brightnessTemporaryValue {
cell.detailTextLabel?.text = brightnessStrings[Int(brightness/10)]
} else {
cell.detailTextLabel?.text = brightnessStrings[brightnessStrings.count - 1]
}
cell.accessoryType = .disclosureIndicator
}
}
func doneButtonHandler(bluetoothPeripheral: BluetoothPeripheral?) {
if let bluetoothPeripheral = bluetoothPeripheral as? M5Stack {
// store value of textColor
if let textColor = textColorTemporaryValue {
bluetoothPeripheral.textcolor = Int32(textColor.rawValue)
}
// store value of backGroundColor
if let backGroundColor = backGroundColorTemporaryValue {
bluetoothPeripheral.backGroundColor = Int32(backGroundColor.rawValue)
}
// store value of rotation
if let rotation = rotationTempValue {
bluetoothPeripheral.rotation = Int32(rotation)
}
// store value of brightness
if let brightness = brightnessTemporaryValue {
bluetoothPeripheral.brightness = Int16(brightness)
}
}
}
func screenTitle() -> String {
Texts_M5StackView.screenTitle
}
/// - parameters :
/// - bluetoothTransmitterDelegate : usually the uiViewController
/// - bluetoothPeripheral : if nil then the viewcontroller is opened to scan for a new peripheral
/// - bluetoothPeripheralManager : reference to bluetoothPeripheralManaging object
/// - tableView : needed to intiate refresh of row
/// - bluetoothPeripheralViewController : BluetoothPeripheralViewController
/// - settingRowOffset :needs to be set to number of generic settings in BluetoothPeripheralViewController
func configure(bluetoothPeripheral: BluetoothPeripheral?, bluetoothPeripheralManager: BluetoothPeripheralManaging, tableView: UITableView, bluetoothPeripheralViewController: BluetoothPeripheralViewController, settingRowOffset:Int, bluetoothTransmitterDelegate: BluetoothTransmitterDelegate) {
self.bluetoothPeripheralManager = bluetoothPeripheralManager
self.tableView = tableView
self.bluetoothPeripheralViewController = bluetoothPeripheralViewController
self.settingRowOffset = settingRowOffset
self.bluetoothTransmitterDelegate = bluetoothTransmitterDelegate
if let bluetoothPeripheral = bluetoothPeripheral as? M5Stack {
storeTempValues(from: bluetoothPeripheral)
}
}
}

View File

@ -1,7 +1,7 @@
import UIKit
import CoreBluetooth
/// uiviewcontroller to show list of M5Stacks, first uiviewcontroller when clicking the M5Stack tab
/// uiviewcontroller to show list of BluetoothPeripherals, first uiviewcontroller when clicking the BluetoothPeripheral tab
final class BluetoothPeripheralsViewController: UIViewController {
// MARK: - IBOutlet's and IBAction's
@ -14,8 +14,8 @@ final class BluetoothPeripheralsViewController: UIViewController {
@IBAction func unwindToBluetoothPeripheralsViewController (segue: UIStoryboardSegue) {
// reinitialise m5Stacks because we're coming back from M5StackViewController where an M5Stack may have been added or deleted
initializeM5Stacks()
// reinitialise bluetoothPeripherals because we're coming back from BluetoothPeripheralViewController where a BluetoothPeripheral may have been added or deleted
initializeBluetoothPeripherals()
// reload the table
tableView.reloadSections(IndexSet(integer: 0), with: .none)
@ -27,11 +27,8 @@ final class BluetoothPeripheralsViewController: UIViewController {
private var coreDataManager:CoreDataManager?
/// a bluetoothPeripheralManager
private weak var bluetoothPeripheralManager: BluetoothPeripheralManaging?
/// list of M5Stack's
private var m5Stacks: [M5Stack] = []
private weak var bluetoothPeripheralManager: BluetoothPeripheralManaging!
// MARK: public functions
/// configure
@ -40,7 +37,7 @@ final class BluetoothPeripheralsViewController: UIViewController {
// initalize private properties
self.coreDataManager = coreDataManager
self.bluetoothPeripheralManager = bluetoothPeripheralManager
initializeM5Stacks()
initializeBluetoothPeripherals()
}
@ -50,9 +47,10 @@ final class BluetoothPeripheralsViewController: UIViewController {
super.viewDidLoad()
title = Texts_M5StacksView.screenTitle
title = Texts_BluetoothPeripheralsView.screenTitle
setupView()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@ -67,12 +65,13 @@ final class BluetoothPeripheralsViewController: UIViewController {
switch segueIdentifierAsCase {
case BluetoothPeripheralViewController.SegueIdentifiers.M5StacksToM5StackSegueIdentifier:
guard let vc = segue.destination as? BluetoothPeripheralViewController, let coreDataManager = coreDataManager, let bluetoothPeripheralManager = bluetoothPeripheralManager else {
fatalError("In BluetoothPeripheralsViewController, prepare for segue, viewcontroller is not M5StackViewController or coreDataManager is nil or bluetoothPeripheralManager is nil" )
case BluetoothPeripheralViewController.SegueIdentifiers.BluetoothPeripheralsToBluetoothPeripheralSegueIdentifier:
guard let vc = segue.destination as? BluetoothPeripheralViewController, let coreDataManager = coreDataManager else {
fatalError("In BluetoothPeripheralsViewController, prepare for segue, viewcontroller is not BluetoothPeripheralViewController or coreDataManager is nil" )
}
vc.configure(m5Stack: sender as? M5Stack, coreDataManager: coreDataManager, bluetoothPeripheralManager: bluetoothPeripheralManager)
vc.configure(bluetoothPeripheral: sender as? BluetoothPeripheral, coreDataManager: coreDataManager, bluetoothPeripheralManager: bluetoothPeripheralManager, expectedBluetoothPeripheralType: .M5Stack)
}
}
@ -83,8 +82,8 @@ final class BluetoothPeripheralsViewController: UIViewController {
/// user clicked add button
private func addButtonAction() {
/// go to screen to add a new M5Stack
self.performSegue(withIdentifier: BluetoothPeripheralViewController.SegueIdentifiers.M5StacksToM5StackSegueIdentifier.rawValue, sender: nil)
/// go to screen to add a new BluetoothPeripheral
self.performSegue(withIdentifier: BluetoothPeripheralViewController.SegueIdentifiers.BluetoothPeripheralsToBluetoothPeripheralSegueIdentifier.rawValue, sender: nil)
}
@ -109,30 +108,27 @@ final class BluetoothPeripheralsViewController: UIViewController {
}
}
/// calls tableView.reloadRows for the row where forM5Stack is shown
private func updateRow(forM5Stack m5Stack: M5Stack) {
/// calls tableView.reloadRows for the row where bluetoothPeripheral is shown
private func updateRow(for bluetoothPeripheral: BluetoothPeripheral) {
if let index = m5Stacks.firstIndex(of: m5Stack) {
if let index = bluetoothPeripheralManager.getBluetoothPeripherals().firstIndex(where: {$0.getAddress() == bluetoothPeripheral.getAddress()}) {
tableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
}
}
/// initializes m5Stacks array
private func initializeM5Stacks() {
/// initializes bluetoothPeripherals
/// - sets the delegates of each transmitter to self
private func initializeBluetoothPeripherals() {
if let bluetoothPeripheralManager = bluetoothPeripheralManager {
m5Stacks = bluetoothPeripheralManager.m5Stacks()
for bluetoothtTransmitter in bluetoothPeripheralManager.getBluetoothTransmitters() {
for m5Stack in m5Stacks {
bluetoothPeripheralManager.m5StackBluetoothTransmitter(forM5stack: m5Stack, createANewOneIfNecesssary: false)?.m5StackBluetoothTransmitterDelegateVariable = self
}
bluetoothtTransmitter.variableBluetoothTransmitterDelegate = self
} else {// should never happen or it would be a coding error, but let's assign to empty string
m5Stacks = []
}
}
}
// MARK: - extensions
@ -144,7 +140,7 @@ extension BluetoothPeripheralsViewController: UITableViewDataSource, UITableView
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// add 1 for the row that will show help info
return m5Stacks.count + 1
return bluetoothPeripheralManager.getBluetoothPeripherals().count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@ -152,27 +148,27 @@ extension BluetoothPeripheralsViewController: UITableViewDataSource, UITableView
guard let cell = tableView.dequeueReusableCell(withIdentifier: SettingsTableViewCell.reuseIdentifier, for: indexPath) as? SettingsTableViewCell else { fatalError("Unexpected Table View Cell") }
// the last row is the help info
if indexPath.row == m5Stacks.count {
if indexPath.row == bluetoothPeripheralManager.getBluetoothPeripherals().count {
cell.textLabel?.text = Texts_M5StacksView.m5StackSoftWareHelpCellText
cell.textLabel?.text = Texts_M5StackView.m5StackSoftWareHelpCellText
cell.detailTextLabel?.text = nil
cell.accessoryType = .disclosureIndicator
return cell
}
// textLabel should be the userdefinedname of the M5Stack, or if userdefinedname == nil, then the address
cell.textLabel?.text = m5Stacks[indexPath.row].m5StackName?.userDefinedName
// textLabel should be the user defined alias of the BluetoothPeripheral, or if user defined alias == nil, then the devicename
cell.textLabel?.text = bluetoothPeripheralManager.getBluetoothPeripherals()[indexPath.row].getAlias()
if cell.textLabel?.text == nil {
cell.textLabel?.text = m5Stacks[indexPath.row].address
cell.textLabel?.text = bluetoothPeripheralManager.getBluetoothPeripherals()[indexPath.row].getDeviceName()
}
// detail is the connection status
cell.detailTextLabel?.text = Texts_M5StackView.notConnected // start with not connected
if let bluetoothTransmitter = bluetoothPeripheralManager?.m5StackBluetoothTransmitter(forM5stack: m5Stacks[indexPath.row], createANewOneIfNecesssary: false) {
cell.detailTextLabel?.text = Text_BluetoothPeripheralView.notConnected // start with not connected
if let bluetoothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: bluetoothPeripheralManager.getBluetoothPeripherals()[indexPath.row], createANewOneIfNecesssary: false) {
if let connectionStatus = bluetoothTransmitter.getConnectionStatus(), connectionStatus == CBPeripheralState.connected {
cell.detailTextLabel?.text = Texts_M5StackView.connected
cell.detailTextLabel?.text = Text_BluetoothPeripheralView.connected
}
}
@ -184,7 +180,7 @@ extension BluetoothPeripheralsViewController: UITableViewDataSource, UITableView
}
func numberOfSections(in tableView: UITableView) -> Int {
// only 1 section, namely the list of M5Stacks
// only 1 section, namely the list of BluetoothPeripherals
return 1
}
@ -193,15 +189,15 @@ extension BluetoothPeripheralsViewController: UITableViewDataSource, UITableView
tableView.deselectRow(at: indexPath, animated: true)
// the last row is the help info
if indexPath.row == m5Stacks.count {
if indexPath.row == bluetoothPeripheralManager.getBluetoothPeripherals().count {
let alert = UIAlertController(title: Texts_HomeView.info, message: Texts_M5StacksView.m5StackSoftWareHelpText + " " + ConstantsM5Stack.githubURLM5Stack, actionHandler: nil)
let alert = UIAlertController(title: Texts_HomeView.info, message: Texts_M5StackView.m5StackSoftWareHelpText + " " + ConstantsM5Stack.githubURLM5Stack, actionHandler: nil)
self.present(alert, animated: true, completion: nil)
} else {
self.performSegue(withIdentifier: BluetoothPeripheralViewController.SegueIdentifiers.M5StacksToM5StackSegueIdentifier.rawValue, sender: m5Stacks[indexPath.row])
self.performSegue(withIdentifier: BluetoothPeripheralViewController.SegueIdentifiers.BluetoothPeripheralsToBluetoothPeripheralSegueIdentifier.rawValue, sender: bluetoothPeripheralManager.getBluetoothPeripherals()[indexPath.row])
}
@ -209,65 +205,31 @@ extension BluetoothPeripheralsViewController: UITableViewDataSource, UITableView
}
// MARK: extension M5StackBluetoothDelegate
// MARK: - extension BluetoothTransmitterDelegate
extension BluetoothPeripheralsViewController: M5StackBluetoothDelegate {
extension BluetoothPeripheralsViewController: BluetoothTransmitterDelegate {
func isAskingForAllParameters(m5Stack: M5Stack) {
// viewcontroller doesn't use this
}
func isReadyToReceiveData(m5Stack: M5Stack) {
// viewcontroller doesn't use this
}
func newBlePassWord(newBlePassword: String, forM5Stack m5Stack: M5Stack) {
func didConnectTo(bluetoothTransmitter: BluetoothTransmitter) {
// blePassword is also saved in BluetoothPerpheralManager, tant pis
m5Stack.blepassword = newBlePassword
updateRow(for: bluetoothPeripheralManager.getBluetoothPeripheral(for: bluetoothTransmitter))
}
func authentication(success: Bool, forM5Stack m5Stack: M5Stack) {
// no further handling, means when this view is open, user won't see that authentication is failing
}
func blePasswordMissing(forM5Stack m5Stack: M5Stack) {
// no further handling, means when this view is open, user won't see that ble password is missing
}
func m5StackResetRequired(forM5Stack m5Stack: M5Stack) {
// no further handling, means when this view is open, user won't see that reset is required
}
func didConnect(forM5Stack m5Stack: M5Stack?, address: String?, name: String?, bluetoothTransmitter: M5StackBluetoothTransmitter) {
func didDisconnectFrom(bluetoothTransmitter: BluetoothTransmitter) {
if let m5Stack = m5Stack {
updateRow(forM5Stack: m5Stack)
}
updateRow(for: bluetoothPeripheralManager.getBluetoothPeripheral(for: bluetoothTransmitter))
}
func didDisconnect(forM5Stack m5Stack: M5Stack) {
updateRow(forM5Stack: m5Stack)
}
func deviceDidUpdateBluetoothState(state: CBManagerState, forM5Stack m5Stack: M5Stack) {
func deviceDidUpdateBluetoothState(state: CBManagerState, bluetoothTransmitter: BluetoothTransmitter) {
// when bluetooth status changes to powered off, the device, if connected, will disconnect, however didDisConnect doesn't get call (looks like an error in iOS) - so let's reload the cell that shows the connection status, this will refresh the cell
if state == CBManagerState.poweredOff {
updateRow(forM5Stack: m5Stack)
updateRow(for: bluetoothPeripheralManager.getBluetoothPeripheral(for: bluetoothTransmitter))
}
}
func error(message: String) {
// no further handling, means when this view is open, user won't see the error message
}
}