Watlaa connection, master mode (ie watlaa is master) - receives Libre packets and watlaa battery level

This is a test version

To setup Watlaa :
- go to settings tab, select transmitter type Watlaa
- go back to home screen
- click transmitter
- click start sensor and start the sensor
- then go to the bluetooth tab (the second tab)
- click the +
- select watlaa
- select watlaa master
- scan
- click 'done'

xdrip should now receive the libre packets, you should get a calibration request
This commit is contained in:
Johan Degraeve 2020-01-12 00:16:23 +01:00
parent 586737fe13
commit 2ec60c502f
17 changed files with 468 additions and 108 deletions

View File

@ -69,6 +69,8 @@
F830992823C32A13005741DF /* TextsWatlaaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F830992723C32A13005741DF /* TextsWatlaaView.swift */; };
F830992A23C32A98005741DF /* WatlaaView.strings in Resources */ = {isa = PBXBuildFile; fileRef = F830992923C32A98005741DF /* WatlaaView.strings */; };
F830992C23C694F4005741DF /* WatlaaAccessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F830992B23C694F4005741DF /* WatlaaAccessor.swift */; };
F830992E23C7D756005741DF /* WatlaaBluetoothTransmitterMaster+CGMTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F830992D23C7D756005741DF /* WatlaaBluetoothTransmitterMaster+CGMTransmitter.swift */; };
F830993023C928E0005741DF /* WatlaaBluetoothTransmitterDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F830992F23C928E0005741DF /* WatlaaBluetoothTransmitterDelegate.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 */; };
@ -328,6 +330,8 @@
F830992723C32A13005741DF /* TextsWatlaaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextsWatlaaView.swift; sourceTree = "<group>"; };
F830992923C32A98005741DF /* WatlaaView.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = WatlaaView.strings; sourceTree = "<group>"; };
F830992B23C694F4005741DF /* WatlaaAccessor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatlaaAccessor.swift; sourceTree = "<group>"; };
F830992D23C7D756005741DF /* WatlaaBluetoothTransmitterMaster+CGMTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WatlaaBluetoothTransmitterMaster+CGMTransmitter.swift"; sourceTree = "<group>"; };
F830992F23C928E0005741DF /* WatlaaBluetoothTransmitterDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatlaaBluetoothTransmitterDelegate.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>"; };
@ -904,6 +908,8 @@
isa = PBXGroup;
children = (
F830991F23C291E2005741DF /* WatlaaBluetoothTransmitterMaster.swift */,
F830992D23C7D756005741DF /* WatlaaBluetoothTransmitterMaster+CGMTransmitter.swift */,
F830992F23C928E0005741DF /* WatlaaBluetoothTransmitterDelegate.swift */,
);
path = watlaa;
sourceTree = "<group>";
@ -1548,10 +1554,10 @@
isa = PBXGroup;
children = (
F8F971F623A5915900C3F17D /* M5StackBluetoothTransmitter.swift */,
F8F971FE23A5915900C3F17D /* M5StackBluetoothTransmitterDelegate.swift */,
F8F971FB23A5915900C3F17D /* M5StackMessages */,
F8F971F723A5915900C3F17D /* M5StackTransmitterOpCode.swift */,
F8F971F823A5915900C3F17D /* Utilities */,
F8F971FB23A5915900C3F17D /* M5StackMessages */,
F8F971FE23A5915900C3F17D /* M5StackBluetoothTransmitterDelegate.swift */,
);
path = M5Stack;
sourceTree = "<group>";
@ -1923,6 +1929,7 @@
F8A1586722EDB8BF007F5B5D /* ConstantsHomeView.swift in Sources */,
F8A1585922EDB7C6007F5B5D /* ConstantsDefaultAlertLevels.swift in Sources */,
F8A54AAD22D6859200934E7A /* SlopeParameters.swift in Sources */,
F830993023C928E0005741DF /* WatlaaBluetoothTransmitterDelegate.swift in Sources */,
F8B3A783225D37F2004BA588 /* TextsNightScoutTestResult.swift in Sources */,
F8025C0A21D94FD700ECF0C0 /* CBManagerState.swift in Sources */,
F8F9722723A5915900C3F17D /* CGMBluconTransmitter.swift in Sources */,
@ -1930,6 +1937,7 @@
F8F9721823A5915900C3F17D /* CGMBlueReaderTransmitter.swift in Sources */,
F8A1586D22EDB9BE007F5B5D /* ConstantsDexcomFollower.swift in Sources */,
F8F9720923A5915900C3F17D /* AESCrypt.m in Sources */,
F830992E23C7D756005741DF /* WatlaaBluetoothTransmitterMaster+CGMTransmitter.swift in Sources */,
F80ED2ED236F68F90005C035 /* SettingsViewM5StackGeneralSettingsViewModel.swift in Sources */,
F8B3A850227F26F8004BA588 /* AlertTypesSettingsViewController.swift in Sources */,
F8B3A808227A2933004BA588 /* SettingsSelectedRowAction.swift in Sources */,

View File

@ -14,7 +14,7 @@ enum BluetoothPeripheralType: String, CaseIterable {
case M5StickCType = "M5StickC"
/// watlaa master
case watlaaMaster = "watlaaMaster"
case watlaaMaster = "Watlaa master"
/// - returns: the BluetoothPeripheralViewModel
func viewModel() -> BluetoothPeripheralViewModel {

View File

@ -6,7 +6,7 @@ import CoreBluetooth
/// Most of the functions are already defined by BlueToothTransmitter.swift - so most of these functions don't need re-implementation in CGMTransmitter classes that conform to this protocol.
///
/// An exception is for example initiatePairing, which is implemented in CGMG5Transmitter.swift, because that transmitter needs to send a message to the transmitter that will cause the app to request the user to accept the pairing
protocol CGMTransmitter {
protocol CGMTransmitter:AnyObject {
/// get device address, cgmtransmitters should also derive from BlueToothTransmitter, hence no need to implement this function
///
@ -85,6 +85,9 @@ enum CGMTransmitterType:String, CaseIterable {
/// BlueReader
case blueReader = "BlueReader"
/// watlaa
case watlaa = "Watlaa (under development)"
/// does the transmitter need a transmitter id ?
///
/// can be used in UI stuff, if reset not possible then there's no need to show that option in the settings UI
@ -112,6 +115,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return false
case .watlaa:
return false
}
}
@ -145,6 +151,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return false
case .watlaa:
return false
}
}
@ -177,6 +185,10 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return false
case .watlaa:
return false
}
}
@ -216,6 +228,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return nil
case .watlaa:
return nil
}
}
@ -247,6 +261,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelBlueReader
case .watlaa:
return ConstantsDefaultAlertLevels.defaultBatteryAlertLevelWatlaa
}
}
@ -278,6 +295,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return false
case .watlaa:
return false
}
}
@ -306,6 +326,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return "%"
case .watlaa:
return "%"
}
}
@ -334,6 +357,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .blueReader:
return false
case .watlaa:
return false
}
}
}

View File

@ -48,6 +48,10 @@ protocol CGMTransmitterDelegate:AnyObject {
/// to pass some text error message, delegate can decide to show to user, log, ...
func error(message: String)
/// temporary function till all cgm transmitters have moved to bluetooth tab. - this function returns the currently assigned cgmTransmiter
func getCGMTransmitter() -> CGMTransmitter?
}

View File

@ -154,7 +154,7 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
centralManager.cancelPeripheralConnection(peripheral)
}
}
}
/// stops scanning
@ -237,6 +237,11 @@ class BluetoothTransmitter: NSObject, CBCentralManagerDelegate, CBPeripheralDele
return false
}
}
/// calls peripheral?.readValue(for: characteristic)
func readValueForCharacteristic(for characteristic: CBCharacteristic) {
peripheral?.readValue(for: characteristic)
}
/// will write to characteristicToWriteTo
/// - returns: true if writeValue was successfully called, doesn't necessarily mean data is successvully written to peripheral

View File

@ -398,10 +398,8 @@ final class M5StackBluetoothTransmitter: BluetoothTransmitter {
case .readBatteryLevelRx:
guard value.count >= 2 else {
trace(" value length should be minimum 2", log: log, type: .error)
return
}
let receivedBatteryLevel = Int(value[1])

View File

@ -0,0 +1,11 @@
import Foundation
protocol WatlaaBluetoothTransmitterDelegate: BluetoothTransmitterDelegate {
/// will be called if WatlaaBluetoothTransmitter is connected and ready to receive data, as soon as this is received, xdrip can request for example battery level
func isReadyToReceiveData(watlaaBluetoothTransmitter: WatlaaBluetoothTransmitterMaster)
/// Watlaa is sending batteryLevel
func receivedBattery(level: Int, watlaaBluetoothTransmitter: WatlaaBluetoothTransmitterMaster)
}

View File

@ -0,0 +1,21 @@
import Foundation
extension WatlaaBluetoothTransmitterMaster: CGMTransmitter {
func initiatePairing() {
// no pairing needed for watlaa
}
func reset(requested: Bool) {
// no reset need for watlaa
}
func setWebOOPEnabled(enabled: Bool) {
// no web oop for watlaa as sensorid detection not supported
}
func setWebOOPSiteAndToken(oopWebSite: String, oopWebToken: String) {
// no web oop for watlaa as sensorid detection not supported
}
}

View File

@ -2,7 +2,7 @@ import Foundation
import os
import CoreBluetooth
class WatlaaBluetoothTransmitterMaster: BluetoothTransmitter {
final class WatlaaBluetoothTransmitterMaster: BluetoothTransmitter {
// MARK: UUID's
@ -16,7 +16,7 @@ class WatlaaBluetoothTransmitterMaster: BluetoothTransmitter {
let CBUUID_CurrentTime_Service = "00002A2B-0000-1000-8000-00805F9B34FB"
/// characteristic uuids (created them in an enum as there's a lot of them, it's easy to switch through the list)
private enum CBUUID_Characteristic_UUID:String, CustomStringConvertible {
private enum CBUUID_Characteristic_UUID:String, CustomStringConvertible, CaseIterable {
/// Raw data characteristic
case CBUUID_RawData_Characteristic = "00001011-1212-EFDE-0137-875F45AC0113"
@ -76,32 +76,137 @@ class WatlaaBluetoothTransmitterMaster: BluetoothTransmitter {
/// will be used to pass back bluetooth and cgm related events
private weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
/// receive buffer for bubble packets
private var rxBuffer:Data
/// used when processing Bubble data packet
private var startDate:Date
// used in parsing packet
private var timeStampLastBgReading:Date
/// battery level Characteristic, needed to be able to read value
private var batteryLevelCharacteric: CBCharacteristic?
// MARK: - public functions
/// - 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
init(address:String?, name: String?, cgmTransmitterDelegate:CGMTransmitterDelegate?, bluetoothTransmitterDelegate: M5StackBluetoothTransmitterDelegate, bluetoothPeripheralType: BluetoothPeripheralType) {
init(address:String?, name: String?, cgmTransmitterDelegate:CGMTransmitterDelegate?, bluetoothTransmitterDelegate: WatlaaBluetoothTransmitterDelegate, bluetoothPeripheralType: BluetoothPeripheralType) {
// assign addressname and name or expected devicename
var newAddressAndName:BluetoothTransmitter.DeviceAddressAndName = BluetoothTransmitter.DeviceAddressAndName.notYetConnected(expectedName: "watlaa")
if let address = address {
newAddressAndName = BluetoothTransmitter.DeviceAddressAndName.alreadyConnectedBefore(address: address, name: name)
}
// initialize rxbuffer
rxBuffer = Data()
startDate = Date()
//assign CGMTransmitterDelegate
self.cgmTransmitterDelegate = cgmTransmitterDelegate
//initialize timeStampLastBgReading
timeStampLastBgReading = Date(timeIntervalSince1970: 0)
// initialize - CBUUID_Receive_Authentication.rawValue and CBUUID_Write_Control.rawValue will not be used in the superclass
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_Data_Service), CBUUID(string: CBUUID_Battery_Service), CBUUID(string: CBUUID_CurrentTime_Service)], CBUUID_ReceiveCharacteristic: CBUUID_Characteristic_UUID.CBUUID_RawData_Characteristic.rawValue, CBUUID_WriteCharacteristic: CBUUID_Characteristic_UUID.CBUUID_Calibration_Characteristic.rawValue, startScanningAfterInit: false, bluetoothTransmitterDelegate: bluetoothTransmitterDelegate)
//assign CGMTransmitterDelegate
self.cgmTransmitterDelegate = cgmTransmitterDelegate
}
/// read battery level
public func readBatteryLevel() {
if let batteryLevelCharacteric = batteryLevelCharacteric {
readValueForCharacteristic(for: batteryLevelCharacteric)
}
}
// MARK: - private functions
/// reset rxBuffer, reset startDate, stop packetRxMonitorTimer, set resendPacketCounter to 0
private func resetRxBuffer() {
rxBuffer = Data()
startDate = Date()
}
/// creates CBUUID_Characteristic_UUID for the characteristicUUID
private func receivedCharacteristicUUIDToCharacteristic(characteristicUUID:String) -> CBUUID_Characteristic_UUID? {
// using enum to make sure no new characteristics are forgotten in case new are added in the future
for characteristic_UUID in CBUUID_Characteristic_UUID.allCases {
switch characteristic_UUID {
case .CBUUID_RawData_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_RawData_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_RawData_Characteristic
}
case .CBUUID_BridgeConnectionStatus_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_BridgeConnectionStatus_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_BridgeConnectionStatus_Characteristic
}
case .CBUUID_LastBGRawValue_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_LastBGRawValue_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_LastBGRawValue_Characteristic
}
case .CBUUID_Calibration_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_Calibration_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_Calibration_Characteristic
}
case .CBUUID_GlucoseUnit_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_GlucoseUnit_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_GlucoseUnit_Characteristic
}
case .CBUUID_AlertSettings_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_AlertSettings_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_AlertSettings_Characteristic
}
case .CBUUID_BatteryLevel_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_BatteryLevel_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_BatteryLevel_Characteristic
}
case .CBUUID_CurrentTime_Characteristic:
if CBUUID_Characteristic_UUID.CBUUID_CurrentTime_Characteristic.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_CurrentTime_Characteristic
}
}
}
return nil
}
private func handleUpdateValueFor_RawData_Characteristic(value: Data) {
rxBuffer.append(value)
if rxBuffer.count >= 344 {
if (Crc.LibreCrc(data: &rxBuffer, headerOffset: 0)) {
// setting webOOPEnabled to false, as we don't have the sensor serial number
LibreDataParser.libreDataProcessor(sensorSerialNumber: nil, webOOPEnabled: false, oopWebSite: nil, oopWebToken: nil, libreData: (rxBuffer.subdata(in: 0..<(344 + 0))), 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()
}
}
}
@ -127,6 +232,17 @@ class WatlaaBluetoothTransmitterMaster: BluetoothTransmitter {
private func handleUpdateValueFor_BatteryLevel_Characteristic(value: Data) {
guard value.count >= 1 else {
trace(" value length should be minimum 1", log: log, type: .error)
return
}
// Watlaa is sending batteryLevel, which is in the first byte
let receivedBatteryLevel = Int(value[0])
(fixedBluetoothTransmitterDelegate as? WatlaaBluetoothTransmitterDelegate)?.receivedBattery(level: receivedBatteryLevel, watlaaBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? WatlaaBluetoothTransmitterDelegate)?.receivedBattery(level: receivedBatteryLevel, watlaaBluetoothTransmitter: self)
}
private func handleUpdateValueFor_CurrentTime_Characteristic(value: Data) {
@ -135,23 +251,45 @@ class WatlaaBluetoothTransmitterMaster: BluetoothTransmitter {
// MARK: - BluetoothTransmitter overriden functions
override func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
super.peripheral(peripheral, didDiscoverCharacteristicsFor: service, error: error)
//need to store some of the characteristics to be able to write to them
if let characteristics = service.characteristics {
for characteristic in characteristics {
if (characteristic.uuid == CBUUID(string: CBUUID_Characteristic_UUID.CBUUID_BatteryLevel_Characteristic.rawValue)) {
trace(" found batteryLevelCharacteristic", log: log, type: .info)
batteryLevelCharacteric = characteristic
}
}
}
// here all characteristics should be known, we can call isReadyToReceiveData
(fixedBluetoothTransmitterDelegate as? WatlaaBluetoothTransmitterDelegate)?.isReadyToReceiveData(watlaaBluetoothTransmitter: self)
(variableBluetoothTransmitterDelegate as? WatlaaBluetoothTransmitterDelegate)?.isReadyToReceiveData(watlaaBluetoothTransmitter: self)
}
override func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
super.peripheral(peripheral, didUpdateValueFor: characteristic, error: error)
// find the CBUUID_Characteristic_UUID
guard let characteristic_UUID = CBUUID_Characteristic_UUID(rawValue: characteristic.uuid.uuidString) else {
guard let receivedCharacteristic = receivedCharacteristicUUIDToCharacteristic(characteristicUUID: characteristic.uuid.uuidString) else {
trace("in peripheralDidUpdateValueFor, unknown characteristic received with uuid = %{public}@", log: log, type: .error, characteristic.uuid.uuidString)
return
}
trace("in peripheralDidUpdateValueFor, characteristic uuid = %{public}@", log: log, type: .info, characteristic_UUID.description)
trace("in peripheralDidUpdateValueFor, characteristic uuid = %{public}@", log: log, type: .info, receivedCharacteristic.description)
guard let value = characteristic.value else {return}
debuglogging("Received value from Watlaa : " + value.hexEncodedString())
switch characteristic_UUID {
switch receivedCharacteristic {
case .CBUUID_RawData_Characteristic:
handleUpdateValueFor_RawData_Characteristic(value: value)

View File

@ -9,6 +9,7 @@ enum ConstantsDefaultAlertLevels {
static let defaultBatteryAlertLevelBlucon = 20
static let defaultBatteryAlertLevelDroplet = 20
static let defaultBatteryAlertLevelBlueReader = 20
static let defaultBatteryAlertLevelWatlaa = 20
// blood glucose level alert values in mgdl
static let veryHigh = 250

View File

@ -42,6 +42,9 @@ public class M5Stack: NSManagedObject {
// this is creation of an M5Stack, not M5Stick, set isM5StickC to false
self.isM5StickC = false
// by default, don't connect to WiFi
self.connectToWiFi = false
}
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {

View File

@ -5,6 +5,9 @@ public class Watlaa: NSManagedObject {
/// explanation, see function parameterUpdateNotNeededAtNextConnect in protocol BluetoothPeripheral
public var parameterUpdateNeeded: Bool = false
/// battery level , not stored in coredata,
public var batteryLevel: Int?
/// create Watlaa, shouldconnect default value = true
/// - parameters:

View File

@ -40,12 +40,17 @@ class BluetoothPeripheralManager: NSObject {
/// to solve problem that sometemes UserDefaults key value changes is triggered twice for just one change
private let keyValueObserverTimeKeeper:KeyValueObserverTimeKeeper = KeyValueObserverTimeKeeper()
/// will be used to pass back bluetooth and cgm related events
/// will be used to pass back bluetooth and cgm related events, probably temporary ?
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
/// when xdrip connects to a BluetoothTransmitter that is also CGMTransmitter, then we'll call this function with the BluetoothTransmitter as argument. This is to let the cgmTransmitterDelegate know what is the CGMTransmitter
private var onCGMTransmitterCreation: (CGMTransmitter?) -> ()
// MARK: - initializer
init(coreDataManager: CoreDataManager, cgmTransmitterDelegate: CGMTransmitterDelegate) {
/// - parameters:
/// - onCGMTransmitterCreation : to be called when cgmtransmitter is created
init(coreDataManager: CoreDataManager, cgmTransmitterDelegate: CGMTransmitterDelegate, onCGMTransmitterCreation: @escaping (CGMTransmitter?) -> ()) {
// initialize properties
self.coreDataManager = coreDataManager
@ -53,6 +58,7 @@ class BluetoothPeripheralManager: NSObject {
self.bgReadingsAccessor = BgReadingsAccessor(coreDataManager: coreDataManager)
self.watlaaAccessor = WatlaaAccessor(coreDataManager: coreDataManager)
self.cgmTransmitterDelegate = cgmTransmitterDelegate
self.onCGMTransmitterCreation = onCGMTransmitterCreation
super.init()
@ -187,6 +193,92 @@ class BluetoothPeripheralManager: NSObject {
// MARK: - private functions
/// check if transmitter in bluetoothTransmitters with index, is the cgmtransmitter currently assigned to delegate, if so set cgmtransmitter at delegate to nil - This should be temporary till cgm transmitters have moved to bluetooth tab
private func setCGMTransmitterToNilAtDelegate(withIndexInBluetoothTransmitters index: Int) {
if let cgmTransmitter = cgmTransmitterDelegate?.getCGMTransmitter() as? BluetoothTransmitter, let transmitterBeingDeleted = bluetoothTransmitters[index] {
if cgmTransmitter.getAddress() == transmitterBeingDeleted.getAddress() {
// so the cgmTransmitter is actually being deleted, so we need to also assign cgmTransmitterDelegate to nil to make sure there's no more reference to it
onCGMTransmitterCreation(nil)
}
}
}
/// 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 connectToWiFi
if !m5StackBluetoothTransmitter.writeConnectToWiFi(connect: m5Stack.connectToWiFi) {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
}
/// when user changes M5Stack related settings, then the transmitter need to get that info, add observers
private func addObservers() {
@ -214,7 +306,7 @@ class BluetoothPeripheralManager: NSObject {
if let bluetoothTransmitter = getBluetoothTransmitter(for: bluetoothPeripheral, createANewOneIfNecesssary: false) {
bluetoothTransmitter.disconnect(reconnectAfterDisconnect: false)
_ = bluetoothTransmitter.disconnect(reconnectAfterDisconnect: false)
}
@ -376,9 +468,7 @@ class BluetoothPeripheralManager: NSObject {
}
// MARK: - extensions
// MARK: extension BluetoothPeripheralManaging
// MARK: - conform to BluetoothPeripheralManaging
extension BluetoothPeripheralManager: BluetoothPeripheralManaging {
@ -504,6 +594,9 @@ extension BluetoothPeripheralManager: BluetoothPeripheralManaging {
return
}
// check if transmitter being deleted is assigned to cgmTransmitterDelegate, if so we need to set it also to nil, otherwise the bluetoothTransmitter deinit function wouldn't get called
setCGMTransmitterToNilAtDelegate(withIndexInBluetoothTransmitters: index)
// set bluetoothTransmitter to nil, this will also initiate a disconnect
bluetoothTransmitters[index] = nil
@ -546,6 +639,9 @@ extension BluetoothPeripheralManager: BluetoothPeripheralManaging {
if let index = firstIndexInBluetoothPeripherals(bluetoothPeripheral: bluetoothPeripheral) {
// check if transmitter being deleted is assigned to cgmTransmitterDelegate, if so we need to set it also to nil, otherwise the bluetoothTransmitter deinit function wouldn't get called
setCGMTransmitterToNilAtDelegate(withIndexInBluetoothTransmitters: index)
bluetoothTransmitters[index] = nil
}
@ -553,12 +649,17 @@ extension BluetoothPeripheralManager: BluetoothPeripheralManaging {
}
// MARK: extension M5StackBluetoothDelegate
// MARK: - conform to BluetoothTransmitterDelegate
extension BluetoothPeripheralManager: BluetoothTransmitterDelegate {
func didConnectTo(bluetoothTransmitter: BluetoothTransmitter) {
// temporary,
if bluetoothTransmitter is CGMTransmitter {
onCGMTransmitterCreation((bluetoothTransmitter as! CGMTransmitter))
}
// 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)
@ -635,7 +736,28 @@ extension BluetoothPeripheralManager: BluetoothTransmitterDelegate {
}
// MARK: conform to M5StackBluetoothTransmitterDelegate
// MARK: - conform to WatlaaBluetoothTransmitterDelegate
extension BluetoothPeripheralManager: WatlaaBluetoothTransmitterDelegate {
func isReadyToReceiveData(watlaaBluetoothTransmitter: WatlaaBluetoothTransmitterMaster) {
// request battery level
watlaaBluetoothTransmitter.readBatteryLevel()
}
func receivedBattery(level: Int, watlaaBluetoothTransmitter: WatlaaBluetoothTransmitterMaster) {
guard let index = bluetoothTransmitters.firstIndex(of: watlaaBluetoothTransmitter), let watlaa = bluetoothPeripherals[index] as? Watlaa else {return}
watlaa.batteryLevel = level
}
}
// MARK: - conform to M5StackBluetoothTransmitterDelegate
extension BluetoothPeripheralManager: M5StackBluetoothTransmitterDelegate {
@ -722,7 +844,7 @@ extension BluetoothPeripheralManager: M5StackBluetoothTransmitterDelegate {
}
/// bluetoothPeripheral is asking for an update of all parameters, send them
/// M5Stack is asking for an update of all parameters, send them
func isAskingForAllParameters(m5StackBluetoothTransmitter: M5StackBluetoothTransmitter) {
guard let index = bluetoothTransmitters.firstIndex(of: m5StackBluetoothTransmitter) else {
@ -761,77 +883,6 @@ extension BluetoothPeripheralManager: M5StackBluetoothTransmitterDelegate {
}
// 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 connectToWiFi
if !m5StackBluetoothTransmitter.writeConnectToWiFi(connect: m5Stack.connectToWiFi) {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

@ -19,7 +19,7 @@
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>3179</string>
<string>3233</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View File

@ -516,7 +516,7 @@ class M5StackBluetoothPeripheralViewModel {
}
// MARK: - extension BluetoothTransmitterDelegate
// MARK: - conform to BluetoothTransmitterDelegate
extension M5StackBluetoothPeripheralViewModel: BluetoothTransmitterDelegate {
@ -535,7 +535,7 @@ extension M5StackBluetoothPeripheralViewModel: BluetoothTransmitterDelegate {
}
// MARK: - extension M5StackBluetoothTransmitterDelegate
// MARK: - conform to M5StackBluetoothTransmitterDelegate
extension M5StackBluetoothPeripheralViewModel: M5StackBluetoothTransmitterDelegate {
@ -773,12 +773,12 @@ extension M5StackBluetoothPeripheralViewModel: BluetoothPeripheralViewModel {
self.bluetoothTransmitterDelegate = bluetoothTransmitterDelegate
if let m5StackPeripheral = bluetoothPeripheral as? M5Stack {
if let m5Stack = bluetoothPeripheral as? M5Stack {
storeTempValues(from: m5StackPeripheral)
storeTempValues(from: m5Stack)
// also request batteryLevel, this may have been updated
if let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: m5StackPeripheral, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = blueToothTransmitter as? M5StackBluetoothTransmitter {
if let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: m5Stack, createANewOneIfNecesssary: false), let m5StackBluetoothTransmitter = blueToothTransmitter as? M5StackBluetoothTransmitter {
_ = m5StackBluetoothTransmitter.readBatteryLevel()

View File

@ -5,6 +5,14 @@ class WatlaaMasterBluetoothPeripheralViewModel {
// MARK: - private properties
/// settings correspond to watlaa specific rows in viewcontroller
private enum WatlaaSetting: Int, CaseIterable {
/// batteryLevel
case batteryLevel
}
/// reference to bluetoothPeripheralManager
private weak var bluetoothPeripheralManager: BluetoothPeripheralManaging?
@ -19,7 +27,7 @@ class WatlaaMasterBluetoothPeripheralViewModel {
}
// MARK: - extension BluetoothPeripheralViewModelProtocol
// MARK: - conform to BluetoothPeripheralViewModel
extension WatlaaMasterBluetoothPeripheralViewModel: BluetoothPeripheralViewModel {
@ -37,6 +45,13 @@ extension WatlaaMasterBluetoothPeripheralViewModel: BluetoothPeripheralViewModel
storeTempValues(from: watlaaPeripheral)
// also request batteryLevel, this may have been updated
if let blueToothTransmitter = bluetoothPeripheralManager.getBluetoothTransmitter(for: watlaaPeripheral, createANewOneIfNecesssary: false), let watlaaBluetoothTransmitter = blueToothTransmitter as? WatlaaBluetoothTransmitterMaster {
_ = watlaaBluetoothTransmitter.readBatteryLevel()
}
}
}
@ -51,19 +66,66 @@ extension WatlaaMasterBluetoothPeripheralViewModel: BluetoothPeripheralViewModel
func update(cell: UITableViewCell, forRow rawValue: Int, forSection section: Int, for bluetoothPeripheral: BluetoothPeripheral, doneButtonOutlet: UIBarButtonItem) {
// verify that bluetoothPeripheralAsNSObject is an M5Stack
guard let watlaa = bluetoothPeripheral as? Watlaa else {
fatalError("WatlaaMasterBluetoothPeripheralViewModel update, bluetoothPeripheral is not Watlaa")
}
// default value for accessoryView is nil
cell.accessoryView = nil
switch section {
case 1:
guard let setting = WatlaaSetting(rawValue: rawValue) else { fatalError("WatlaaMasterBluetoothPeripheralViewModel update, unexpected setting") }
switch setting {
case .batteryLevel:
cell.textLabel?.text = Texts_BluetoothPeripheralsView.batteryLevel
cell.detailTextLabel?.text = watlaa.batteryLevel?.description
cell.accessoryType = .none
}
default:
fatalError("in WatlaaMasterBluetoothPeripheralViewModel update, unhandled section number")
}
}
func userDidSelectRow(withSettingRawValue rawValue: Int, forSection section: Int, for bluetoothPeripheral: BluetoothPeripheral, bluetoothPeripheralManager: BluetoothPeripheralManaging, doneButtonOutlet: UIBarButtonItem) {
switch section {
case 1:
guard let setting = WatlaaSetting(rawValue: rawValue) else { fatalError("WatlaaMasterBluetoothPeripheralViewModel userDidSelectRow, unexpected setting") }
switch setting {
case .batteryLevel:
// user can't do anything by clicking on battery row
break
}
default:
fatalError("in WatlaaMasterBluetoothPeripheralViewModel update, unhandled section number")
}
}
func numberOfSettings(inSection section: Int) -> Int {
return 0
return WatlaaSetting.allCases.count
}
func numberOfSections() -> Int {
// for the moment only one specific section for watlaa
return 0
return 1
}
func storeTempValues(from bluetoothPeripheral: BluetoothPeripheral) {
@ -75,7 +137,7 @@ extension WatlaaMasterBluetoothPeripheralViewModel: BluetoothPeripheralViewModel
}
// MARK: - extension BluetoothTransmitterDelegate
// MARK: - conform to BluetoothTransmitterDelegate
extension WatlaaMasterBluetoothPeripheralViewModel: BluetoothTransmitterDelegate {
@ -94,3 +156,19 @@ extension WatlaaMasterBluetoothPeripheralViewModel: BluetoothTransmitterDelegate
}
// MARK: - conform to WatlaaBluetoothTransmitterDelegate
extension WatlaaMasterBluetoothPeripheralViewModel: WatlaaBluetoothTransmitterDelegate {
func isReadyToReceiveData(watlaaBluetoothTransmitter: WatlaaBluetoothTransmitterMaster) {
// viewcontroller doesn't use this
}
func receivedBattery(level: Int, watlaaBluetoothTransmitter: WatlaaBluetoothTransmitterMaster) {
// batteryLevel should get updated in Watlaa object by bluetoothPeripheralManager, here's the trigger to update the table
tableView?.reloadRows(at: [IndexPath(row: WatlaaSetting.batteryLevel.rawValue, section: 1)], with: .none)
}
}

View File

@ -406,7 +406,12 @@ final class RootViewController: UIViewController {
})
// setup bluetoothPeripheralManager
bluetoothPeripheralManager = BluetoothPeripheralManager(coreDataManager: coreDataManager, cgmTransmitterDelegate: self)
bluetoothPeripheralManager = BluetoothPeripheralManager(coreDataManager: coreDataManager, cgmTransmitterDelegate: self, onCGMTransmitterCreation: {
(cgmTransmitter: CGMTransmitter?) in
self.cgmTransmitter = cgmTransmitter
})
}
@ -811,6 +816,10 @@ final class RootViewController: UIViewController {
case .blueReader:
cgmTransmitter = CGMBlueReaderTransmitter(address: UserDefaults.standard.cgmTransmitterDeviceAddress, name: UserDefaults.standard.cgmTransmitterDeviceName, delegate: self)
case .watlaa:
// watlaa transmitter needs to be set through bluetooth devices tab
cgmTransmitter = nil
}
// assign calibrator
@ -818,7 +827,7 @@ final class RootViewController: UIViewController {
case .dexcomG4, .dexcomG5, .dexcomG6:
calibrator = DexcomCalibrator()
case .miaomiao, .GNSentry, .Blucon, .Bubble, .Droplet1, .blueReader:
case .miaomiao, .GNSentry, .Blucon, .Bubble, .Droplet1, .blueReader, .watlaa:
// for all transmitters used with Libre1, calibrator is either NoCalibrator or Libre1Calibrator, depending if oopWeb is supported by the transmitter and on value of webOOPEnabled in settings
calibrator = RootViewController.getCalibrator(transmitterType: selectedTransmitterType, webOOPEnabled: UserDefaults.standard.webOOPEnabled)
}
@ -1318,6 +1327,10 @@ final class RootViewController: UIViewController {
/// conform to CGMTransmitterDelegate
extension RootViewController:CGMTransmitterDelegate {
func getCGMTransmitter() -> CGMTransmitter? {
return cgmTransmitter
}
func error(message: String) {
let alert = UIAlertController(title: Texts_Common.warning, message: message, actionHandler: nil)