connects to M5 stack

This commit is contained in:
Johan Degraeve 2019-09-12 08:37:35 +02:00
parent 514e21f62b
commit b44be99f83
23 changed files with 797 additions and 93 deletions

View File

@ -73,6 +73,14 @@
F8A1586F22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */; };
F8A1587122EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */; };
F8A1587322EDC893007F5B5D /* ConstantsDexcomShare.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */; };
F8A389BA2315DC060010F405 /* M5StackBluetoothTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */; };
F8A389BC231713580010F405 /* M5StackTransmitterOpCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */; };
F8A389BF231DC8790010F405 /* M5StackAuthenticateTXMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389BE231DC8790010F405 /* M5StackAuthenticateTXMessage.swift */; };
F8A389C3231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C2231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift */; };
F8A389C6231FBA110010F405 /* M5StackPacket.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C5231FBA110010F405 /* M5StackPacket.swift */; };
F8A389C823203E3E0010F405 /* ConstantsM5Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C723203E3E0010F405 /* ConstantsM5Stack.swift */; };
F8A389CA232118E80010F405 /* M5StackDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389C9232118E80010F405 /* M5StackDelegate.swift */; };
F8A389CC232191280010F405 /* M5StackUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A389CB232191280010F405 /* M5StackUtilities.swift */; };
F8A54AAD22D6859200934E7A /* SlopeParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AAC22D6859200934E7A /* SlopeParameters.swift */; };
F8A54AAF22D686CD00934E7A /* NightScoutBgReading.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AAE22D686CD00934E7A /* NightScoutBgReading.swift */; };
F8A54AB722D9111900934E7A /* CGMTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AB322D9111900934E7A /* CGMTransmitter.swift */; };
@ -271,6 +279,14 @@
F8A1586E22EDC7EE007F5B5D /* ConstantsSuspensionPrevention.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSuspensionPrevention.swift; sourceTree = "<group>"; };
F8A1587022EDC865007F5B5D /* ConstantsSpeakReadingLanguages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsSpeakReadingLanguages.swift; sourceTree = "<group>"; };
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsDexcomShare.swift; sourceTree = "<group>"; };
F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackBluetoothTransmitter.swift; sourceTree = "<group>"; };
F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackTransmitterOpCode.swift; sourceTree = "<group>"; };
F8A389BE231DC8790010F405 /* M5StackAuthenticateTXMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackAuthenticateTXMessage.swift; sourceTree = "<group>"; };
F8A389C2231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackReadBlePassWordTxMessage.swift; sourceTree = "<group>"; };
F8A389C5231FBA110010F405 /* M5StackPacket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackPacket.swift; sourceTree = "<group>"; };
F8A389C723203E3E0010F405 /* ConstantsM5Stack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConstantsM5Stack.swift; sourceTree = "<group>"; };
F8A389C9232118E80010F405 /* M5StackDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackDelegate.swift; sourceTree = "<group>"; };
F8A389CB232191280010F405 /* M5StackUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = M5StackUtilities.swift; sourceTree = "<group>"; };
F8A54AAC22D6859200934E7A /* SlopeParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SlopeParameters.swift; sourceTree = "<group>"; };
F8A54AAE22D686CD00934E7A /* NightScoutBgReading.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightScoutBgReading.swift; sourceTree = "<group>"; };
F8A54AB322D9111900934E7A /* CGMTransmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGMTransmitter.swift; sourceTree = "<group>"; };
@ -771,6 +787,7 @@
F85DC2FC21D3E83100B9F74A /* Transmitter */ = {
isa = PBXGroup;
children = (
F8A389B82315DB080010F405 /* M5Stack */,
F8025E5521F3418400ECF0C0 /* CGMBluetoothTransmitter */,
F85DC2FF21D3E83100B9F74A /* GenericBluetoothTransmitter */,
);
@ -786,6 +803,36 @@
path = GenericBluetoothTransmitter;
sourceTree = "<group>";
};
F8A389B82315DB080010F405 /* M5Stack */ = {
isa = PBXGroup;
children = (
F8A389C4231FB9B30010F405 /* Utilities */,
F8A389BD231DC7DC0010F405 /* M5StackMessages */,
F8A389B92315DC060010F405 /* M5StackBluetoothTransmitter.swift */,
F8A389BB231713580010F405 /* M5StackTransmitterOpCode.swift */,
);
path = M5Stack;
sourceTree = "<group>";
};
F8A389BD231DC7DC0010F405 /* M5StackMessages */ = {
isa = PBXGroup;
children = (
F8A389BE231DC8790010F405 /* M5StackAuthenticateTXMessage.swift */,
F8A389C2231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift */,
);
path = M5StackMessages;
sourceTree = "<group>";
};
F8A389C4231FB9B30010F405 /* Utilities */ = {
isa = PBXGroup;
children = (
F8A389C5231FBA110010F405 /* M5StackPacket.swift */,
F8A389C9232118E80010F405 /* M5StackDelegate.swift */,
F8A389CB232191280010F405 /* M5StackUtilities.swift */,
);
path = Utilities;
sourceTree = "<group>";
};
F8A54AB222D9111900934E7A /* Generic */ = {
isa = PBXGroup;
children = (
@ -1157,6 +1204,7 @@
F8A1587222EDC893007F5B5D /* ConstantsDexcomShare.swift */,
F856CE5A22EDC8E50083E436 /* ConstantsBluetoothPairing.swift */,
F8EEDD5122FECE3800D2D610 /* ConstantsLibreOOP.swift */,
F8A389C723203E3E0010F405 /* ConstantsM5Stack.swift */,
);
name = Constants;
path = xdrip/Constants;
@ -1390,6 +1438,7 @@
F8A54ADF22D911BA00934E7A /* DexcomTransmitterOpCode.swift in Sources */,
F81D6D4E22BFC762005EFAE2 /* TextsDexcomShareTestResult.swift in Sources */,
F8A54AE922D911BA00934E7A /* AuthChallengeRxMessage.swift in Sources */,
F8A389BA2315DC060010F405 /* M5StackBluetoothTransmitter.swift in Sources */,
F8A54B0122D9179100934E7A /* LibreSensorState.swift in Sources */,
F8EA6C8221B723BC0082976B /* Date.swift in Sources */,
F8A54AE622D911BA00934E7A /* KeepAliveTxMessage.swift in Sources */,
@ -1427,6 +1476,7 @@
F81D6D4822BD5F62005EFAE2 /* DexcomShareUploadManager.swift in Sources */,
F8A7406E22D9C0E700967CFC /* CGMBluconTransmitter.swift in Sources */,
F8A1586B22EDB967007F5B5D /* ConstantsMaster.swift in Sources */,
F8A389C3231F1A4B0010F405 /* M5StackReadBlePassWordTxMessage.swift in Sources */,
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
F8A54B0022D9179100934E7A /* LibreDataParser.swift in Sources */,
F8025E5421EE8D2100ECF0C0 /* Libre1Calibrator.swift in Sources */,
@ -1470,14 +1520,17 @@
F8B3A81C227DEC92004BA588 /* AlertEntriesAccessor.swift in Sources */,
F8A1585B22EDB7EA007F5B5D /* ConstantsDexcomG5.swift in Sources */,
F8BDD452221DEAB2006EAB84 /* TextsSettingsView.swift in Sources */,
F8A389BF231DC8790010F405 /* M5StackAuthenticateTXMessage.swift in Sources */,
F8EEDD6423020FAD00D2D610 /* NoCalibrator.swift in Sources */,
F8EEDD5422FF685400D2D610 /* NSMutableURLRequest.swift in Sources */,
F897AAFB2201018800CDDD10 /* String.swift in Sources */,
F8A389CA232118E80010F405 /* M5StackDelegate.swift in Sources */,
F8B3A847227F090E004BA588 /* SettingsViewNightScoutSettingsViewModel.swift in Sources */,
F8B3A79622635A25004BA588 /* AlertEntry+CoreDataClass.swift in Sources */,
F8AC425E21ADEBD60078C348 /* AppDelegate.swift in Sources */,
F821CF8E22AB090C005C1E43 /* DatePickerViewController.swift in Sources */,
F8A1585322EDB602007F5B5D /* ConstantsBloodGlucose.swift in Sources */,
F8A389C823203E3E0010F405 /* ConstantsM5Stack.swift in Sources */,
F8A54AE322D911BA00934E7A /* AuthRequestRxMessage.swift in Sources */,
F8A54AD722D911BA00934E7A /* CGMG6Transmitter.swift in Sources */,
F81F9FF822861E6D0028C70F /* KeyValueObserverTimeKeeper.swift in Sources */,
@ -1508,6 +1561,7 @@
F821CF5F229BF43A005C1E43 /* ApplicationManager.swift in Sources */,
F8B3A834227F08AC004BA588 /* PickerViewData.swift in Sources */,
F8B3A79522635A25004BA588 /* AlertType+CoreDataProperties.swift in Sources */,
F8A389BC231713580010F405 /* M5StackTransmitterOpCode.swift in Sources */,
F8B3A84C227F090E004BA588 /* SettingsViewController.swift in Sources */,
F8EEDD5222FECE3800D2D610 /* ConstantsLibreOOP.swift in Sources */,
F8AC426021ADEBD60078C348 /* RootViewController.swift in Sources */,
@ -1520,9 +1574,11 @@
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 */,
F821CF6B229FC22D005C1E43 /* Endpoint.swift in Sources */,
F821CF58229BF43A005C1E43 /* AlertManager.swift in Sources */,
F8A389CC232191280010F405 /* M5StackUtilities.swift in Sources */,
F8025C1121DA5E8F00ECF0C0 /* BluetoothTransmitterDelegate.swift in Sources */,
F8A54AD922D911BA00934E7A /* TransmitterVersionTxMessage.swift in Sources */,
F821CF5E229BF43A005C1E43 /* BgReading+NightScout.swift in Sources */,

View File

@ -51,6 +51,9 @@ enum ConstantsLog {
/// droplet 1
static let categoryCGMDroplet1 = "categoryCGMDroplet1"
/// LibreOOPClient
static let categoryLibreOOPClient = "LibreOOPClient"
static let categoryLibreOOPClient = "categoryLibreOOPClient"
/// for use in M5Stack
static let categoryM5StackBluetoothTransmitter = "categoryM5StackBluetoothTransmitter"
}

View File

@ -0,0 +1,12 @@
import Foundation
enum ConstantsM5Stack {
/// maximum time in milliseconds, between two packets
///
/// this is for the case where M5Stack sends a string to the app, split over multiple packets. There's maximum 17 bytes of useful information per packet. The packets should arrive within a predefined timeframe, otherwise it is to be considered as an error
static let maximumTimeBetweenTwoPacketsInMs = 200
/// maximum BLE packet size
static let maximumMBLEPacketsize = 20
}

View File

@ -58,7 +58,7 @@ enum ConstantsSounds: String, CaseIterable {
return String(forSound.rawValue[soundNameRange])
}
/// gets the soundFie for specific case
/// gets the soundFile for specific case
static func getSoundFile(forSound:ConstantsSounds) -> String {
let indexOfBackSlash = forSound.rawValue.indexes(of: "/")
let soundNameRange = forSound.rawValue.index(after: indexOfBackSlash[0])..<forSound.rawValue.endIndex

View File

@ -10,11 +10,12 @@ extension Date {
return Double(self.timeIntervalSince1970 * 1000)
}
/// returns Date in milliseconds as Int64
/// returns Date in milliseconds as Int64, since 1.1.1970
func toMillisecondsAsInt64() -> Int64 {
return Int64((self.timeIntervalSince1970 * 1000.0).rounded())
}
/// returns Date in seconds as Int64, since 1.1.1970
func toSecondsAsInt64() -> Int64 {
return Int64((self.timeIntervalSince1970).rounded())
}
@ -46,4 +47,9 @@ extension Date {
return dateFormatter.string(from: self).appending("Z")
}
/// returns seconds since 1.1.1970 local time for current timezone
func toSecondsAsInt64Local() -> Int64 {
let calendar = Calendar.current
return (Date().toSecondsAsInt64() + Int64(calendar.timeZone.secondsFromGMT()))
}
}

View File

@ -79,6 +79,11 @@ extension UserDefaults {
/// license info accepted by user yes or no
case licenseInfoAccepted = "licenseInfoAccepted"
// M5Stack
/// M5Stack blepassword, needed for authenticating App to M5Stack
case M5StackBlePassword = "M5StackBlePassword"
// Other Settings (not user configurable)
// Bluetooth
@ -459,7 +464,7 @@ extension UserDefaults {
}
}
// MARK: - ===== Keep track of alert and info messages shown to the user ======
// MARK: - Keep track of alert and info messages shown to the user
/// message shown when user starts a sensor, which tells that timing should be exact, was it already shown or not
var startSensorTimeInfoGiven:Bool {
@ -481,6 +486,18 @@ extension UserDefaults {
}
}
// MARK: M5Stack
/// M5StackBlePassword, used for authenticating xdrip app towards M5Stack
var m5StackBlePassword: String? {
get {
return string(forKey: Key.M5StackBlePassword.rawValue)
}
set {
set(newValue, forKey: Key.M5StackBlePassword.rawValue)
}
}
// MARK: - ===== Other Settings ======
var bluetoothDeviceAddress: String? {

View File

@ -28,12 +28,13 @@ final class CGMG4xDripTransmitter: BluetoothTransmitter, BluetoothTransmitterDel
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID, 5 characters
init(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
init(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: nil)
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
//assign CGMTransmitterDelegate

View File

@ -85,9 +85,6 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
/// is G5 reset necessary or not
private var G5ResetRequested:Bool
// actual device address
private var actualDeviceAddress:String?
/// used as parameter in call to cgmTransmitterDelegate.cgmTransmitterInfoReceived, when there's no glucosedata to send
var emptyArray: [GlucoseData] = []
@ -101,8 +98,9 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID, 6 characters
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
init?(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
//verify if transmitterid is 6 chars and allowed chars
guard transmitterID.count == 6 else {
trace("transmitterID length not 6, init CGMG5Transmitter fails", log: log, type: .error)
@ -119,8 +117,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "DEXCOM" + transmitterID[transmitterID.index(transmitterID.startIndex, offsetBy: 4)..<transmitterID.endIndex])
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
actualDeviceAddress = address
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// set timestampoflastg5reading to 0
@ -282,7 +279,7 @@ class CGMG5Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, CGMTr
// 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 (actualDeviceAddress == nil) {
if (address() == nil) {
_ = startScanning()
}
}

View File

@ -13,10 +13,10 @@ class CGMG6Transmitter: CGMG5Transmitter {
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID, 6 characters
override init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
override init?(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate) {
// call super.init
super.init(address: address, transmitterID: transmitterID, delegate: delegate)
super.init(address: address, name: name, transmitterID: transmitterID, delegate: delegate)
}

View File

@ -24,9 +24,6 @@ class CGMBluconTransmitter: BluetoothTransmitter {
/// for trace
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryBlucon)
// actual device address
private var actualDeviceAddress:String?
// used in parsing packet
private var timeStampLastBgReading:Date
@ -70,18 +67,18 @@ class CGMBluconTransmitter: BluetoothTransmitter {
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID
/// - delegate : CGMTransmitterDelegate
/// - sensorSerialNumber : is needed to allow detection of a new sensor.
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
init?(address:String?, name: String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?) {
// assign addressname and name or expected devicename
// start by using expected device name
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: CGMBluconTransmitter.createExpectedDeviceName(transmitterIdSetByUser: transmitterID))
if let address = address {
// address not nil, means it already connected before, use that address
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
actualDeviceAddress = address
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// initialize timeStampLastBgReading

View File

@ -56,17 +56,18 @@ class CGMBubbleTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate, C
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
/// - delegate : CGMTransmitterDelegate intance
/// - timeStampLastBgReading : timestamp of last bgReading
/// - webOOPEnabled : enabled or not
/// - oopWebSite : oop web site url to use, only used in case webOOPEnabled = true
/// - oopWebToken : oop web token to use, only used in case webOOPEnabled = true
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
init(address:String?, name: String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, sensorSerialNumber:String?, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: expectedDeviceNameBubble)
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// initialize sensorSerialNumber

View File

@ -27,12 +27,13 @@ class CGMDroplet1Transmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// MARK: - Initialization
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
init(address:String?, delegate:CGMTransmitterDelegate) {
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
init(address:String?, name: String?, delegate:CGMTransmitterDelegate) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "limitter")
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// assign CGMTransmitterDelegate

View File

@ -92,12 +92,13 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
init?(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
/// - name: if already connected before, then give here the name that was received during previous connect, if not give nil
init?(address:String?, name: String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "GNSentry")
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
//initialize timeStampLastBgReading

View File

@ -55,17 +55,18 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// MARK: - Initialization
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - name : if already connected before, then give here the name that was received during previous connect, if not give nil
/// - delegate : CGMTransmitterDelegate intance
/// - timeStampLastBgReading : timestamp of last bgReading
/// - webOOPEnabled : enabled or not
/// - oopWebSite : oop web site url to use, only used in case webOOPEnabled = true
/// - oopWebToken : oop web token to use, only used in case webOOPEnabled = true
init(address:String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
init(address:String?, name: String?, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date, webOOPEnabled: Bool, oopWebSite: String, oopWebToken: String) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: expectedDeviceNameMiaoMiao)
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address)
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// assign CGMTransmitterDelegate
@ -93,7 +94,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// MARK: - public functions
func sendStartReadingCommmand() -> Bool {
func sendStartReadingCommand() -> Bool {
if writeDataToPeripheral(data: Data.init([0xF0]), type: .withoutResponse) {
return true
} else {
@ -121,7 +122,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
if error == nil && characteristic.isNotifying {
_ = sendStartReadingCommmand()
_ = sendStartReadingCommand()
}
}
@ -168,7 +169,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
resendPacketCounter = temp + 1
if resendPacketCounter < maxPacketResendRequests {
trace("in peripheral didUpdateValueFor, crc error encountered. New attempt launched", log: log, type: .info)
_ = sendStartReadingCommmand()
_ = sendStartReadingCommand()
} else {
trace("in peripheral didUpdateValueFor, crc error encountered. Maximum nr of attempts reached", log: log, type: .info)
resendPacketCounter = 0
@ -189,7 +190,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
if self.writeDataToPeripheral(data: Data.init([0xD3, 0x01]), type: .withoutResponse) {
trace("in peripheralDidUpdateValueFor, successfully sent 0xD3 and 0x01, confirm sensor change to MiaoMiao", log: self.log, type: .info)
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(500)) {
if !self.sendStartReadingCommmand() {
if !self.sendStartReadingCommand() {
trace("in peripheralDidUpdateValueFor, sendStartReadingCommmand failed", log: self.log, type: .error)
} else {
trace("in peripheralDidUpdateValueFor, successfully sent startReadingCommand to MiaoMiao", log: self.log, type: .info)
@ -245,7 +246,7 @@ class CGMMiaoMiaoTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
// immediately request a new reading
// there's no check here to see if peripheral, characteristic, connection, etc.. exists, but that's no issue. If anything's missing, write will simply fail,
_ = sendStartReadingCommmand()
_ = sendStartReadingCommand()
}
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {

View File

@ -14,6 +14,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// the address of the transmitter. If nil then transmitter never connected, so we don't know the name.
private 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?
@ -74,11 +75,16 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// - 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) {
switch addressAndName {
case .alreadyConnectedBefore(let newAddress):
deviceAddress = newAddress
case .alreadyConnectedBefore(let addressAndName):
deviceAddress = addressAndName.address
deviceName = addressAndName.name
case .notYetConnected(let newexpectedName):
expectedName = newexpectedName
}
//assign uuid's
@ -118,7 +124,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
func disconnect() {
if let peripheral = peripheral {
if let centralManager = centralManager {
trace("disconnect, disconnecting", log: log, type: .info)
trace("disconnect, disconnecting, for peripheral with name %{public}@", log: log, type: .info, deviceName ?? "'unknown'")
centralManager.cancelPeripheralConnection(peripheral)
}
}
@ -126,6 +132,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// start bluetooth scanning for device
func startScanning() -> BluetoothTransmitter.startScanningResult {
trace("in startScanning", log: log, type: .info)
//assign default returnvalue
@ -182,11 +189,11 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// - returns: true if writeValue was successfully called, doesn't necessarily mean data is successvully written to peripheral
func writeDataToPeripheral(data:Data, type:CBCharacteristicWriteType) -> Bool {
if let peripheral = peripheral, let writeCharacteristic = writeCharacteristic {
trace("in writeDataToPeripheral", log: log, type: .info)
trace("in writeDataToPeripheral, for peripheral with name %{public}@, characteristic = %{public}@, data = %{public}@", log: log, type: .info, deviceName ?? "'unknown'", writeCharacteristic.uuid.uuidString, data.hexEncodedString())
peripheral.writeValue(data, for: writeCharacteristic, type: type)
return true
} else {
trace("in writeDataToPeripheral, failed because either peripheral or writeCharacteristic is nil", log: log, type: .error)
trace("in writeDataToPeripheral, for peripheral with name %{public}@, failed because either peripheral or writeCharacteristic is nil", log: log, type: .error, deviceName ?? "'unknown'")
return false
}
}
@ -195,11 +202,11 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// - returns: true if writeValue was successfully called, doesn't necessarily mean data is successvully written to peripheral
func writeDataToPeripheral(data:Data, characteristicToWriteTo:CBCharacteristic, type:CBCharacteristicWriteType) -> Bool {
if let peripheral = peripheral {
trace("in writeDataToPeripheral for characteristic %{public}@", log: log, type: .info, characteristicToWriteTo.uuid.description)
trace("in writeDataToPeripheral, for peripheral with name %{public}@, for characteristic %{public}@", log: log, type: .info, deviceName ?? "'unknown'", characteristicToWriteTo.uuid.description)
peripheral.writeValue(data, for: characteristicToWriteTo, type: type)
return true
} else {
trace("in writeDataToPeripheral, failed because either peripheral or characteristicToWriteTo is nil", log: log, type: .error)
trace("in writeDataToPeripheral, for peripheral with name %{public}@, failed because either peripheral or characteristicToWriteTo is nil", log: log, type: .error, deviceName ?? "'unknown'")
return false
}
}
@ -207,10 +214,10 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// calls setNotifyValue for characteristic with value enabled
func setNotifyValue(_ enabled: Bool, for characteristic: CBCharacteristic) {
if let peripheral = peripheral {
trace("setNotifyValue, setting notify for characteristic %{public}@, to %{public}@", log: log, type: .info, characteristic.uuid.uuidString, enabled.description)
trace("setNotifyValue, for peripheral with name %{public}@, setting notify for characteristic %{public}@, to %{public}@", log: log, type: .info, deviceName ?? "'unknown'", characteristic.uuid.uuidString, enabled.description)
peripheral.setNotifyValue(enabled, for: characteristic)
} else {
trace("setNotifyValue, failed to set notify for characteristic %{public}@, to %{public}@", log: log, type: .error, characteristic.uuid.uuidString, enabled.description)
trace("setNotifyValue, for peripheral with name %{public}@, failed to set notify for characteristic %{public}@, to %{public}@", log: log, type: .error, deviceName ?? "'unknown'", characteristic.uuid.uuidString, enabled.description)
}
}
@ -243,24 +250,23 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// the result of the attempt to try to find such device, is returned
fileprivate func retrievePeripherals(_ central:CBCentralManager) -> Bool {
if let deviceAddress = deviceAddress {
debuglogging("in retrievePeripherals, deviceaddress is not nil")
trace("in retrievePeripherals, deviceaddress is %{public}@", log: log, type: .info, deviceAddress)
if let uuid = UUID(uuidString: deviceAddress) {
debuglogging(" in retrievePeripherals, uuid is not nil")
trace(" uuid is not nil", log: log, type: .info)
var peripheralArr = central.retrievePeripherals(withIdentifiers: [uuid])
if peripheralArr.count > 0 {
peripheral = peripheralArr[0]
if let peripheral = peripheral {
debuglogging(" in retrievePeripherals, peripheral is not nil")
trace("in retrievePeripherals, trying to connect", log: log, type: .info)
trace(" trying to connect", log: log, type: .info)
peripheral.delegate = self
central.connect(peripheral, options: nil)
return true
} else {
debuglogging(" in retrievePeripherals, peripheral isfstart nil")
trace(" peripheral is nil", log: log, type: .info)
}
}
} else {
debuglogging(" in retrievePeripherals, uuid is nil")
trace(" uuid is nil", log: log, type: .info)
}
}
return false
@ -309,7 +315,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
timeStampLastStatusUpdate = Date()
trace("connected, will discover services", log: log, type: .info)
trace("connected, for peripheral with name %{public}@, will discover services", log: log, type: .info, deviceName ?? "'unknown'")
peripheral.discoverServices(servicesCBUUIDs)
bluetoothTransmitterDelegate?.centralManagerDidConnect(address: deviceAddress, name: deviceName)
@ -319,9 +325,9 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
timeStampLastStatusUpdate = Date()
if let error = error {
trace("failed to connect with error: %{public}@, will try again", log: log, type: .error , error.localizedDescription)
trace("failed to connect, for peripheral with name %{public}@, with error: %{public}@, will try again", log: log, type: .error , deviceName ?? "'unknown'", error.localizedDescription)
} else {
trace("failed to connect, will try again", log: log, type: .error)
trace("failed to connect, for peripheral with name %{public}@, will try again", log: log, type: .error, deviceName ?? "'unknown'")
}
centralManager?.connect(peripheral, options: nil)
@ -332,7 +338,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
func centralManagerDidUpdateState(_ central: CBCentralManager) {
timeStampLastStatusUpdate = Date()
trace("in centralManagerDidUpdateState, new state is %{public}@", log: log, type: .info, "\(central.state.toString())")
trace("in centralManagerDidUpdateState, for peripheral with name %{public}@, new state is %{public}@", log: log, type: .info, deviceName ?? "'unknown'", "\(central.state.toString())")
/// 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 {
@ -351,12 +357,12 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
timeStampLastStatusUpdate = Date()
if let error = error {
trace("Did disconnect peripheral with error: %{public}@", log: log, type: .error , error.localizedDescription)
trace("Did disconnect peripheral with name %{public}@, with error: %{public}@", log: log, type: .error , deviceName ?? "'unknown'", error.localizedDescription)
}
// check if automatic reconnect is needed or not
if !reconnectAfterDisconnect {
trace("reconnectAfterDisconnect is false, will not try to reconnect", log: log, type: .info)
trace(" reconnectAfterDisconnect is false, will not try to reconnect", log: log, type: .info)
}
// if self.peripheral == nil, then a manual disconnect or something like that has occured, no need to reconnect
@ -374,7 +380,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
timeStampLastStatusUpdate = Date()
trace("didDiscoverServices", log: log, type: .info)
trace("didDiscoverServices for peripheral with name %{public}@", log: log, type: .info, deviceName ?? "'unknown'")
if let error = error {
trace(" didDiscoverServices error: %{public}@", log: log, type: .error , "\(error.localizedDescription)")
}
@ -392,7 +398,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
timeStampLastStatusUpdate = Date()
trace("didDiscoverCharacteristicsFor for service with uuid %{public}@", log: log, type: .info, String(describing:service.uuid))
trace("didDiscoverCharacteristicsFor for peripheral with name %{public}@, for service with uuid %{public}@", log: log, type: .info, deviceName ?? "'unknown'", String(describing:service.uuid))
if let error = error {
trace(" didDiscoverCharacteristicsFor error: %{public}@", log: log, type: .error , error.localizedDescription)
@ -430,7 +436,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
timeStampLastStatusUpdate = Date()
if let error = error {
trace("didUpdateNotificationStateFor characteristic %{public}@, characteristic description %{public}@, error = %{public}@", log: log, type: .error, String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
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
@ -442,7 +448,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
timeStampLastStatusUpdate = Date()
if let error = error {
trace("didUpdateValueFor characteristic %{public}@, characteristic description %{public}@, error = %{public}@, no further processing", log: log, type: .error, String(describing: characteristic.uuid), String(characteristic.debugDescription), error.localizedDescription)
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)
}
@ -498,6 +504,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
return "connecting"
case .other(let reason):
return "other reason : " + reason
}
}
}
@ -507,12 +514,17 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
/// * For an xDrip bridge, we don't expect a specific devicename, in which case the value stays nil
/// * If we already connected to a device before, then we know it's address
enum DeviceAddressAndName {
/// we already connected to the device so we should know the address
case alreadyConnectedBefore (address:String)
/// we already connected to the device so we should know the address and name
///
/// name kept optional, for in case it was not stored
case alreadyConnectedBefore (address:String, name:String?)
/// * We never connected to the device, so we don't know it's name and address as the Device itself is going to send. We can only have an expected name,
/// * example for G5, if transmitter id is ABCDEF, we expect as devicename DexcomEF.
/// * For an xDrip bridge, we don't expect a specific devicename, in which case the value stays nil
case notYetConnected (expectedName:String?)
}
}

View File

@ -0,0 +1,257 @@
import Foundation
import CoreBluetooth
import os
/// characteristic is 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 {
// MARK: - private properties
/// service to be discovered
private let CBUUID_Service: String = "AF6E5F78-706A-43FB-B1F4-C27D7D5C762F"
/// transmit and receive characteristic
private let CBUUID_TxRxCharacteristic: String = "6D810E9F-0983-4030-BDA7-C7C9A6A19C1C"
/// for trace
private let log = OSLog(subsystem: ConstantsLog.subSystem, category: ConstantsLog.categoryM5StackBluetoothTransmitter)
/// blepassword, used for authenticating xdrip app towards M5Stack
private var blePassword: String?
/// temporary storage for packages received from M5Stack, with the password in it
private var blePasswordM5StackPacket = M5StackPacket()
/// will be used to pass data
private(set) weak var m5StackDelegate:M5StackDelegate?
// MARK: - initializer
/// - parameters:
/// - address : if already conneted before then it should be known
/// - name : if already conneted before then it should be known
init(address:String?, name: String?, delegate:M5StackDelegate, blePassword: String?) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "M5_NightscoutMon")
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
self.blePassword = blePassword
self.m5StackDelegate = delegate
// 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
}
// MARK: public functions
/// writes calculated Value, timestamp and slopeName of the reading as string to M5Stack
/// - reading will be rounded to whole number
/// - timestamp will be sent as long in seconds since 1.1.1970, UTC
/// - slopeName is literally copied
/// reading and timestamp are written in one packet to the M5stack, seperated by blanc, slopeName is written in seperate packet
/// - parameters:
/// - readingValue : the value in mgdl
/// - timeStamp : timestamp of the reading
func writeBgReadingInfo(bgReading: BgReading) {
// create packets to send slopeName
guard let packetWithSlopeName = M5StackUtilities.splitTextInBLEPackets(text: bgReading.slopeName, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.writeSlopeNameTx.rawValue) else {
trace("in writeBgReading, failed to create packets to send slopeName", log: log, type: .error)
return
}
// send slopename
for packet in packetWithSlopeName {
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
trace("in writeBgReading, failed to send packet with slopename", log: log, type: .error)
} else {
trace("successfully written slopename to M5Stack", log: log, type: .error)
}
}
// create text to send, reading value, one blanc, timestamp in seconds
let textToSend = Int(round(bgReading.calculatedValue)).description + " " + bgReading.timeStamp.toSecondsAsInt64().description
// create packets to send reading and timestamp
guard let packetsWithReadingAndTimestamp = M5StackUtilities.splitTextInBLEPackets(text: textToSend, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.bgReadingTx.rawValue) else {
trace("in writeBgReading, failed to create packets to send reading and timestamp", log: log, type: .error)
return
}
// send reading and timestamp
for packet in packetsWithReadingAndTimestamp {
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
trace("in writeBgReading, failed to send packet with reading and timestamp", log: log, type: .error)
} else {
trace("successfully written reading and timestamp to M5Stack", log: log, type: .error)
}
}
}
// MARK: - BluetoothTransmitterDelegate functions
func centralManagerDidConnect(address: String?, name: String?) {
debuglogging("TODO M5StackBluetoothTransmitter centralManagerDidConnect")
}
func centralManagerDidFailToConnect(error: Error?) {
debuglogging("TODO M5StackBluetoothTransmitter centralManagerDidFailToConnect")
}
func centralManagerDidUpdateState(state: CBManagerState) {
debuglogging("TODO M5StackBluetoothTransmitter centralManagerDidUpdateState, new state = " + state.toString())
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
debuglogging("TODO M5StackBluetoothTransmitter centralcentralManagerDidDisconnectPeripheralManagerDidConnect")
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, 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 {
trace("successfully sent authenticateTx to M5Stack", log: log, type: .error)
}
} else {
if !writeDataToPeripheral(data: M5StackReadBlePassWordTxMessage().data, type: .withoutResponse) {
trace("failed to send readBlePassWordTx to M5Stack", log: log, type: .error)
} 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)
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, 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)
return
}
//only for logging
let data = value.hexEncodedString()
trace("in peripheral didUpdateValueFor, data = %{public}@", log: log, type: .debug, data)
// value length should be at least 1
guard value.count > 0 else {
trace(" value length is 0, no further processing", log: log, type: .error)
return
}
guard let opCode = M5StackTransmitterOpCodeRx(rawValue: value[0]) else {
trace(" failed to create opCode", log: log, type: .error)
return
}
trace(" opcode = %{public}@", log: log, type: .info, opCode.description)
switch opCode {
case .readBlePassWordRx:
blePasswordM5StackPacket.addNewPacket(value: value)
if let newBlePassword = blePasswordM5StackPacket.getText() {
self.blePassword = newBlePassword
m5StackDelegate?.newBlePassWord(newBlePassword: newBlePassword)
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
}
case .authenticateSuccessRx:
m5StackDelegate?.authentication(success: true)
// even though not requested, and even if M5Stack may already have it, send the local time
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
case .authenticateFailureRx:
m5StackDelegate?.authentication(success: false)
case .readBlePassWordError1Rx:
m5StackDelegate?.blePasswordMissingInSettings()
case .readBlePassWordError2Rx:
m5StackDelegate?.m5StackResetRequired()
case .readTimeStampRx:
sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack()
}
}
// MARK: helper functions
/// sends local time in seconds since 1.1.1970 and also timeoffset from UTC in seconds. to M5Stack
///
/// local time = UTC time + offset /// UTC time = local time - offset
private func sendLocalTimeAndUTCTimeOffSetInSecondsToM5Stack() {
// NOTE : why creating multiple packets (splitTextInBLEPackets) even though it will all fit in one packet ?
// goal is to be able to send longer strings, to M5 Stack. Then we could create one string which contains the timestamp and offset (split by blanc). But then for sure there will be multiple packets, and I didn't yet develop the logic to reconcatenate strings in the M5Stack
// create local time in seconds as Int64
let localTimeInSeconds = Date().toSecondsAsInt64Local()
// create offset from UTC
let uTCTimeInSeconds = Date().toSecondsAsInt64()
let offSet = localTimeInSeconds - uTCTimeInSeconds
// create packets to send with offset
guard let packetsWithOffset = M5StackUtilities.splitTextInBLEPackets(text: offSet.description, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.writeTimeOffsetTx.rawValue) else {
trace(" failed to create packets for sending offset", log: log, type: .error)
return
}
// send the packets with offset
for packet in packetsWithOffset {
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
trace(" failed to send packet with offset to M5Stack", log: log, type: .error)
} else {
trace(" successfully sent packet with offset to M5Stack", log: log, type: .error)
}
}
// create packets to send with local time
guard let packetsWithLocalTime = M5StackUtilities.splitTextInBLEPackets(text: localTimeInSeconds.description, maxBytesInOneBLEPacket: ConstantsM5Stack.maximumMBLEPacketsize, opCode: M5StackTransmitterOpCodeTx.writeTimeStampTx.rawValue) else {
trace(" failed to create packets for sending timestamp", log: log, type: .error)
return
}
// send packets with localtime
for packet in packetsWithLocalTime {
if !writeDataToPeripheral(data: packet, type: .withoutResponse) {
trace(" failed to send packet with local timestamp to M5Stack", log: log, type: .error)
} else {
trace(" successfully sent packet with local timestamp to M5Stack", log: log, type: .error)
}
}
}
}

View File

@ -0,0 +1,23 @@
import Foundation
struct M5StackAuthenticateTXMessage {
let password: String
var data: Data? {
if password.count == 0 {return nil}
var data = Data([M5StackTransmitterOpCodeTx.authenticateTx.rawValue])
if let passwordAsData = password.data(using: .utf8) {
data.append(passwordAsData)
} else {
return nil
}
return data
}
}

View File

@ -0,0 +1,11 @@
import Foundation
struct M5StackReadBlePassWordTxMessage {
var data: Data {
let data = Data([M5StackTransmitterOpCodeTx.readBlePassWordTx.rawValue])
return data
}
}

View File

@ -0,0 +1,94 @@
import Foundation
/// opcodes for writing to M5Stack
enum M5StackTransmitterOpCodeTx: UInt8, CaseIterable {
/// client writes nightScouturl
case writeNightScoutUrlTx = 0x01
/// client writes nightScoutToken
case writeNightScoutTokenTx = 0x02
/// client writes mgdl, value 0 means mmol is used, value 1 is mgdl
case writemgdlTx = 0x03
/// client writes brightness1, value between 1 and 100
case writebrightness1Tx = 0x04
/// client writes brightness2, value between 1 and 100
case writebrightness2Tx = 0x05
/// client writes brightness3, value between 1 and 100
case writebrightness3Tx = 0x06
/// client writes wlan ssid, 2nd byte in data is used to indicate which wlan (from 1 to 10), next bytes is ssid as string
case writeWlanSSIDTx = 0x07
/// client writes wlan Pass, 2nd byte in data is used to indicate which wlan (from 1 to 10), next bytes is Pass as string
case writeWlanPassTx = 0x08
/// opcode to request the password
///
/// xdrip should only read the password if it doesn't have a password in the settings - M5Stack will send back the password only if it generated a random password during connection, not if it was set in the config ini file. In the latter case, the user should know it and set it in the app settings
case readBlePassWordTx = 0x09
/// authenticate
case authenticateTx = 0x0A
/// bg reading
case bgReadingTx = 0x10
/// writes local time in seconds , seconds since 1.1.1970
case writeTimeStampTx = 0x12
/// write slopeName to M5Stack
case writeSlopeNameTx = 0x13
/// write offset, ie time difference in seconds between local time and utc time
case writeTimeOffsetTx = 0x14
}
/// opcodes for message from M5stack to app
enum M5StackTransmitterOpCodeRx: UInt8, CaseIterable {
/// opcode to receive the password
///
/// xdrip should only read the password if it doesn't have a password in the settings - M5Stack will send back the password only if it generated a random password during first time connection, not if it was set in the config ini file. In the latter case, the user should know it and set it in the app settings
case readBlePassWordRx = 0x0E
/// authenticate
case authenticateSuccessRx = 0x0B
/// authenticate
case authenticateFailureRx = 0x0C
/// readBlePassword failed, error 1 : User should set correct password in settings
case readBlePassWordError1Rx = 0x0D
/// readBlePassword failed, error 2 : Password already known, user should reset M5Stack
case readBlePassWordError2Rx = 0x0F
/// M5Stack requests timestamp in seconds, local time since 1.1.1970 !!
case readTimeStampRx = 0x11
}
extension M5StackTransmitterOpCodeRx: CustomStringConvertible {
public var description: String {
switch self {
case .readBlePassWordRx:
return "readBlePassWordRx"
case .authenticateSuccessRx:
return "authenticateSuccessRx"
case .authenticateFailureRx:
return "authenticateFailureRx"
case .readBlePassWordError1Rx:
return "readBlePassWordError1Rx"
case .readBlePassWordError2Rx:
return "readBlePassWordError2Rx"
case .readTimeStampRx:
return "readTimeStampRx"
}
}
}

View File

@ -0,0 +1,19 @@
import Foundation
protocol M5StackDelegate: AnyObject {
/// if a new ble password is received from M5Stack
func newBlePassWord(newBlePassword: String)
/// 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)
/// there's no ble password set, user should set it in the settings
func blePasswordMissingInSettings()
/// 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()
}

View File

@ -0,0 +1,100 @@
import Foundation
/// M5Stack may send strings in several packets. This class is there to compose to packets and allows to read the resulting string
/// - byte 1 is the total number of packets that must be received before the string is complete
/// - byte 2 is the number of the current packet being added
/// - the remaining bytes represent the string
/// if all packets are received, then the string can be retrieved with the text function
class M5StackPacket {
// MARK: private properties
/// keep track of when last packet was received, if more than x milliseconds, then all data is reset
private var timeStampLastPacket: Date?
/// total number of packets to receive
private var totalNumberOfPacketsToReceive: Int?
/// the packets already received, only if the number of packets equals totalNumberOfPacketsToReceive then we've received all packets
private var packetsReceived: [Data] = []
// MARK: public functions
/// gets the text, only returns a value of all packets were received
func getText() -> String? {
// check that private properties are initialised
guard timeStampLastPacket != nil, let totalNumberOfPacketsToReceive = totalNumberOfPacketsToReceive, packetsReceived.count > 0 else {return nil}
// check if all packets were received
for i in 1...totalNumberOfPacketsToReceive {
if packetsReceived[i - 1].count == 0 {
return nil
}
}
// read the string first as data
var result = Data()
for i in 1...totalNumberOfPacketsToReceive {
result.append(packetsReceived[i - 1])
}
// return the result
return String(bytes: result, encoding: .utf8)
}
/// to be used when new packet received from M5Stack, value is the complete value received from M5Stack, inclusive opcode.
///
/// - byte 1 is the total number of packets that must be received before the string is complete
/// - byte 2 is the number of the current packet being added
/// - the remaining bytes represent the string
/// if all packets are received, then the string can be retrieved with the text function
func addNewPacket(value: Data) {
// length should be at least 3 bytes, otherwise just ignore
if value.count < 3 {return}
// if more than 200 ms ago since last packet, then reset all properties, looks like a new
if let timeStampLastPacket = timeStampLastPacket, Date().toMillisecondsAsInt64() - timeStampLastPacket.toMillisecondsAsInt64() > ConstantsM5Stack.maximumTimeBetweenTwoPacketsInMs {
resetAllProperties()
}
// set timeStampLastPacket to now
timeStampLastPacket = Date()
totalNumberOfPacketsToReceive = Int(value.uint8(position: 2))
guard let totalNumberOfPacketsToReceive = totalNumberOfPacketsToReceive else {return}
// check packetsReceived, possibly still empty which means this would be the first packet being received
if packetsReceived.count == 0 {
// want to have an array of Data, size totalNumberOfPacketsToReceive, actual values will be set as we receive the packets, first one being just a few lines of code later
for _ in 1...totalNumberOfPacketsToReceive {
packetsReceived.append(Data())
}
}
// get number of this packet
let numberOfThisPacket = Int(value.uint8(position: 1))
guard numberOfThisPacket <= totalNumberOfPacketsToReceive else {return}// would be a coding error
// assign data
packetsReceived[numberOfThisPacket - 1] = value.subdata(in: 3..<value.count)
}
// MARK: private functions
/// sets all properties to nil
private func resetAllProperties() {
timeStampLastPacket = nil
packetsReceived = []
totalNumberOfPacketsToReceive = nil
}
}

View File

@ -0,0 +1,62 @@
import Foundation
class M5StackUtilities {
/// for sending text string to a ble peripheral, when text can be longer than maximum packet size.
/// - parameters:
/// - text : text that needs to be sent to peripheral
/// - maxBytesInOneBLEPacket : maximum size of text in one packet, usually 20
/// - opCode : opCode will be literally copied to first byte
/// - returns:
/// - array of data, each element is 1st byte opCode, 2nd byte total number of packets, 3rd byte the number of the packet, then the text utf8 encoded. returns nil if splitting fails, which probably means text could not be converted to data using .utf8
static func splitTextInBLEPackets(text: String, maxBytesInOneBLEPacket: Int, opCode: UInt8) -> [Data]? {
guard let textAsData = text.data(using: .utf8) else {
return nil
}
let sizeOfTextToSend = textAsData.count
var charactersAdded = 0
var totalNumberOfPacketsToSent = sizeOfTextToSend/(maxBytesInOneBLEPacket - 3)
if sizeOfTextToSend > totalNumberOfPacketsToSent * (maxBytesInOneBLEPacket - 3) {
totalNumberOfPacketsToSent += 1
}
var returnValue = [Data]()
// number of the next packet to create
var numberOfNextPacketToSend = 1
while charactersAdded < sizeOfTextToSend {
// calculate size of packet to send
var sizeOfNextPacketToSend = maxBytesInOneBLEPacket
if numberOfNextPacketToSend == totalNumberOfPacketsToSent {
sizeOfNextPacketToSend = 3 + (sizeOfTextToSend - charactersAdded)//First byte = opcode, second byte is packet number, third byte = total number of packets, rest is content
}
var dataToAppend = Data()
dataToAppend.append(opCode)
dataToAppend.append(UInt8(numberOfNextPacketToSend))
dataToAppend.append(UInt8(totalNumberOfPacketsToSent))
for i in 0..<(sizeOfNextPacketToSend - 3) {
dataToAppend.append(textAsData[charactersAdded + i])
}
returnValue.append(dataToAppend)
// increase charactersSent
charactersAdded = charactersAdded + (sizeOfNextPacketToSend - 3);
// increase numberOfNextPacketToSend
numberOfNextPacketToSend += 1
}
return returnValue
}
}

View File

@ -44,6 +44,9 @@ final class RootViewController: UIViewController {
/// outlet for label that shows the current reading
@IBOutlet weak var valueLabelOutlet: UILabel!
/// M5StackBluetoothTransmitter for testing
var m5StackBluetoothTransmitter:M5StackBluetoothTransmitter?
// MARK: - Constants for ApplicationManager usage
/// constant for key in ApplicationManager.shared.addClosureToRunWhenAppWillEnterForeground - create updatelabelstimer
@ -130,8 +133,7 @@ final class RootViewController: UIViewController {
super.viewDidLoad()
// Setup Core Data Manager - setting up coreDataManager happens asynchronously
// completion handler is called when finished. This gives the app time to already continue setup which is independent of coredata, like setting up the transmitter, start scanning
// In the exceptional case that the transmitter would give a new reading before the DataManager is set up, then this new reading will be ignored
// completion handler is called when finished. This gives the app time to already continue setup which is independent of coredata, like initializing the views
coreDataManager = CoreDataManager(modelName: ConstantsCoreData.modelName, completion: {
self.setupApplicationData()
@ -142,6 +144,8 @@ final class RootViewController: UIViewController {
// create transmitter based on UserDefaults
self.initializeCGMTransmitter()
self.m5StackBluetoothTransmitter = M5StackBluetoothTransmitter(address: nil, name: "M5_NightscoutMon", delegate: self, blePassword: nil)
})
// Setup View
@ -272,10 +276,6 @@ final class RootViewController: UIViewController {
/// - sensorTimeInMinutes : should be present only if it's the first reading(s) being processed for a specific sensor and is needed if it's a transmitterType that returns true to the function canDetectNewSensor
private func processNewGlucoseData(glucoseData: inout [GlucoseData], sensorTimeInMinutes: Int?) {
for glucose in glucoseData {
debuglogging(glucose.description)
}
// check that calibrations and coredata manager is not nil
guard let calibrationsAccessor = calibrationsAccessor, let coreDataManager = coreDataManager else {
fatalError("in processNewCGMInfo, calibrations or coreDataManager is nil")
@ -319,7 +319,7 @@ final class RootViewController: UIViewController {
for (_, glucose) in glucoseData.enumerated().reversed() {
if glucose.timeStamp > timeStampLastBgReading {
_ = calibrator.createNewBgReading(rawData: (Double)(glucose.glucoseLevelRaw), filteredData: (Double)(glucose.glucoseLevelRaw), timeStamp: glucose.timeStamp, sensor: activeSensor, last3Readings: &latest3BgReadings, lastCalibrationsForActiveSensorInLastXDays: &lastCalibrationsForActiveSensorInLastXDays, firstCalibration: firstCalibrationForActiveSensor, lastCalibration: lastCalibrationForActiveSensor, deviceName:UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
_ = calibrator.createNewBgReading(rawData: (Double)(glucose.glucoseLevelRaw), filteredData: (Double)(glucose.glucoseLevelRaw), timeStamp: glucose.timeStamp, sensor: activeSensor, last3Readings: &latest3BgReadings, lastCalibrationsForActiveSensorInLastXDays: &lastCalibrationsForActiveSensorInLastXDays, firstCalibration: firstCalibrationForActiveSensor, lastCalibration: lastCalibrationForActiveSensor, deviceName: UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
// save the newly created bgreading permenantly in coredata
coreDataManager.saveChanges()
@ -351,25 +351,29 @@ final class RootViewController: UIViewController {
updateLabels()
}
if let nightScoutUploadManager = nightScoutUploadManager {
nightScoutUploadManager.upload()
}
nightScoutUploadManager?.upload()
if let alertManager = alertManager {
alertManager.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsMaster.maximumBgReadingAgeForAlertsInSeconds)
}
alertManager?.checkAlerts(maxAgeOfLastBgReadingInSeconds: ConstantsMaster.maximumBgReadingAgeForAlertsInSeconds)
if let healthKitManager = healthKitManager {
healthKitManager.storeBgReadings()
}
healthKitManager?.storeBgReadings()
bgReadingSpeaker?.speakNewReading()
if let bgReadingSpeaker = bgReadingSpeaker {
bgReadingSpeaker.speakNewReading()
}
dexcomShareUploadManager?.upload()
if let dexcomShareUploadManager = dexcomShareUploadManager {
dexcomShareUploadManager.upload()
//// temporary code
// get latest reading, ignore sensor, rawdata, timestamp - only 1
let lastReadings = bgReadingsAccessor.getLatestBgReadings(limit: 2, fromDate: nil, forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false)
// if there's no readings, then no further processing
if lastReadings.count > 0 {
let lastReading = lastReadings[0]
// if reading older dan 4.5 minutes, then no further processing
if Date().timeIntervalSince(lastReading.timeStamp) < 4.5 * 60 {
m5StackBluetoothTransmitter?.writeBgReadingInfo(bgReading: lastReading)
}
}
}
}
@ -551,12 +555,12 @@ final class RootViewController: UIViewController {
if let calibrator = self.calibrator {
if latestCalibrations.count == 0 {
// calling initialCalibration will create two calibrations, they are returned also but we don't need them
_ = calibrator.initialCalibration(firstCalibrationBgValue: valueAsDouble, firstCalibrationTimeStamp: Date(timeInterval: -(5*60), since: Date()), secondCalibrationBgValue: valueAsDouble, sensor: activeSensor, lastBgReadingsWithCalculatedValue0AndForSensor: &latestReadings, deviceName:UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
_ = calibrator.initialCalibration(firstCalibrationBgValue: valueAsDouble, firstCalibrationTimeStamp: Date(timeInterval: -(5*60), since: Date()), secondCalibrationBgValue: valueAsDouble, sensor: activeSensor, lastBgReadingsWithCalculatedValue0AndForSensor: &latestReadings, deviceName: UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
} else {
// it's not the first calibration
if let firstCalibrationForActiveSensor = calibrationsAccessor.firstCalibrationForActiveSensor(withActivesensor: activeSensor) {
// calling createNewCalibration will create a new calibrations, it is returned but we don't need it
_ = calibrator.createNewCalibration(bgValue: valueAsDouble, lastBgReading: latestReadings[0], sensor: activeSensor, lastCalibrationsForActiveSensorInLastXDays: &latestCalibrations, firstCalibration: firstCalibrationForActiveSensor, deviceName:UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
_ = calibrator.createNewCalibration(bgValue: valueAsDouble, lastBgReading: latestReadings[0], sensor: activeSensor, lastCalibrationsForActiveSensorInLastXDays: &latestCalibrations, firstCalibration: firstCalibrationForActiveSensor, deviceName: UserDefaults.standard.bluetoothDeviceName, nsManagedObjectContext: coreDataManager.mainManagedObjectContext)
}
}
@ -606,35 +610,35 @@ final class RootViewController: UIViewController {
case .dexcomG4:
if let currentTransmitterId = UserDefaults.standard.transmitterId {
cgmTransmitter = CGMG4xDripTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate:self)
cgmTransmitter = CGMG4xDripTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate:self)
}
case .dexcomG5:
if let currentTransmitterId = UserDefaults.standard.transmitterId {
cgmTransmitter = CGMG5Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self)
cgmTransmitter = CGMG5Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate: self)
}
case .dexcomG6:
if let currentTransmitterId = UserDefaults.standard.transmitterId {
cgmTransmitter = CGMG6Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self)
cgmTransmitter = CGMG6Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate: self)
}
case .miaomiao:
cgmTransmitter = CGMMiaoMiaoTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
cgmTransmitter = CGMMiaoMiaoTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
case .Bubble:
cgmTransmitter = CGMBubbleTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber, webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
cgmTransmitter = CGMBubbleTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber, webOOPEnabled: UserDefaults.standard.webOOPEnabled, oopWebSite: UserDefaults.standard.webOOPSite ?? ConstantsLibreOOP.site, oopWebToken: UserDefaults.standard.webOOPtoken ?? ConstantsLibreOOP.token)
case .GNSentry:
cgmTransmitter = CGMGNSEntryTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
cgmTransmitter = CGMGNSEntryTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
case .Blucon:
if let currentTransmitterId = UserDefaults.standard.transmitterId {
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0), sensorSerialNumber: UserDefaults.standard.sensorSerialNumber)
}
case .Droplet1:
cgmTransmitter = CGMDroplet1Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self)
cgmTransmitter = CGMDroplet1Transmitter(address: UserDefaults.standard.bluetoothDeviceAddress, name: UserDefaults.standard.bluetoothDeviceName, delegate: self)
}
@ -868,6 +872,14 @@ final class RootViewController: UIViewController {
}
}
// add action to start scanning for m5stack
listOfActions["M5Stack"] = {(UIAlertAction) in
debuglogging("calling m5StackBluetoothTransmitter")
self.m5StackBluetoothTransmitter?.startScanning()
}
// next action is to start or stop the sensor, can also be omitted depending on type of device - also not applicable for follower mode
if let transmitterType = UserDefaults.standard.transmitterType {
if !transmitterType.canDetectNewSensor() && UserDefaults.standard.isMaster {
@ -1139,6 +1151,7 @@ extension RootViewController:CGMTransmitterDelegate {
UserDefaults.standard.lastdisConnectTimestamp = Date()
case .poweredOn:
// user changes device bluetooth status to on
debuglogging("in deviceDidUpdateBluetoothState, status = poweredon")
if UserDefaults.standard.bluetoothDeviceAddress == nil, let cgmTransmitter = cgmTransmitter, let transmitterType = UserDefaults.standard.transmitterType, transmitterType.startScanningAfterInit() {
// bluetoothDeviceAddress = nil, means app hasn't connected before to the transmitter
// cgmTransmitter != nil, means user has configured transmitter type and transmitterid
@ -1146,7 +1159,7 @@ extension RootViewController:CGMTransmitterDelegate {
// possibly scanning is already running, but that's ok if we call the startScanning function again
_ = cgmTransmitter.startScanning()
}
@unknown default:
break
}
@ -1455,4 +1468,24 @@ extension RootViewController:NightScoutFollowerDelegate {
}
}
extension RootViewController: M5StackDelegate {
func newBlePassWord(newBlePassword: String) {
UserDefaults.standard.m5StackBlePassword = newBlePassword
}
func authentication(success: Bool) {
debuglogging("successfully authenticated towards M5Stack")
}
func blePasswordMissingInSettings() {
debuglogging("ble password missing in settings")
}
func m5StackResetRequired() {
debuglogging("M5 Stack reset required")
}
}