Blucon : up to pairing done

This commit is contained in:
Johan Degraeve 2019-07-14 15:37:50 +02:00
parent b320789a5b
commit 40c12b25ea
8 changed files with 232 additions and 13 deletions

View File

@ -87,6 +87,7 @@
F8A54AFF22D9179100934E7A /* CRC.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AFC22D9179100934E7A /* CRC.swift */; };
F8A54B0022D9179100934E7A /* ParseLibreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AFD22D9179100934E7A /* ParseLibreData.swift */; };
F8A54B0122D9179100934E7A /* SensorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A54AFE22D9179100934E7A /* SensorState.swift */; };
F8A7406E22D9C0E700967CFC /* CGMBluconTransmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A7406D22D9C0E700967CFC /* CGMBluconTransmitter.swift */; };
F8AC425E21ADEBD60078C348 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AC425D21ADEBD60078C348 /* AppDelegate.swift */; };
F8AC426021ADEBD60078C348 /* RootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8AC425F21ADEBD60078C348 /* RootViewController.swift */; };
F8AC426521ADEBD60078C348 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F8AC426321ADEBD60078C348 /* Main.storyboard */; };
@ -252,6 +253,7 @@
F8A54AFD22D9179100934E7A /* ParseLibreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParseLibreData.swift; sourceTree = "<group>"; };
F8A54AFE22D9179100934E7A /* SensorState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SensorState.swift; sourceTree = "<group>"; };
F8A54B0A22D9215500934E7A /* xdrip-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "xdrip-Bridging-Header.h"; sourceTree = "<group>"; };
F8A7406D22D9C0E700967CFC /* CGMBluconTransmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGMBluconTransmitter.swift; sourceTree = "<group>"; };
F8AC425A21ADEBD60078C348 /* xdrip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = xdrip.app; sourceTree = BUILT_PRODUCTS_DIR; };
F8AC425D21ADEBD60078C348 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
F8AC425F21ADEBD60078C348 /* RootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootViewController.swift; sourceTree = "<group>"; };
@ -798,6 +800,7 @@
F8A54AF522D9156600934E7A /* Blucon */ = {
isa = PBXGroup;
children = (
F8A7406D22D9C0E700967CFC /* CGMBluconTransmitter.swift */,
);
path = Blucon;
sourceTree = "<group>";
@ -1278,6 +1281,7 @@
F8A54ADB22D911BA00934E7A /* AuthRequestTxMessage.swift in Sources */,
F8EA6CA921BBE3010082976B /* UniqueId.swift in Sources */,
F81D6D4822BD5F62005EFAE2 /* DexcomShareUploadManager.swift in Sources */,
F8A7406E22D9C0E700967CFC /* CGMBluconTransmitter.swift in Sources */,
F8B3A7B2226A0878004BA588 /* TextsAlerts.swift in Sources */,
F8A54B0022D9179100934E7A /* ParseLibreData.swift in Sources */,
F8025E5421EE8D2100ECF0C0 /* Libre1Calibrator.swift in Sources */,

View File

@ -39,6 +39,7 @@ struct Constants {
static let defaultBatteryAlertLevelDexcomG4 = 210
static let defaultBatteryAlertLevelMiaoMiao = 20
static let defaultBatteryAlertLevelGNSEntry = 20
static let defaultBatteryAlertLevelBlucon = 20
// blood glucose level alert values in mgdl
static let veryHigh = 250
@ -82,6 +83,8 @@ struct Constants {
static let categoryCGMG5 = "categoryCGMG5"
/// GNSEntry
static let categoryCGMGNSEntry = "categoryCGMGNSEntry"
/// Blucon
static let categoryBlucon = "categoryBlucon"
/// core data manager
static let categoryCoreDataManager = "categoryCoreDataManager"
/// application data bgreadings
@ -379,4 +382,11 @@ struct Constants {
}
/// constants related to Bluetooth pairing
enum BluetoothPairing {
/// minimum time in seconds between two pairing notifications
static let minimumTimeBetweenTwoPairingNotificationsInSeconds = 30
}
}

View File

@ -61,6 +61,9 @@ enum CGMTransmitterType:String, CaseIterable {
/// GNSentry
case GNSentry = "GNSentry"
/// Blucon
case Blucon = "Blucon"
/// 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
@ -79,6 +82,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .GNSentry:
return false
case .Blucon:
return true
}
}
@ -101,6 +106,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .GNSentry:
return false
case .Blucon:
return false
}
}
@ -133,6 +140,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .miaomiao, .GNSentry:
return nil
case .Blucon:
// todo: validate transmitter id for blucon
return nil
}
}
@ -152,6 +162,9 @@ enum CGMTransmitterType:String, CaseIterable {
case .GNSentry:
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelGNSEntry
case .Blucon:
return Constants.DefaultAlertLevels.defaultBatteryAlertLevelBlucon
}
}
@ -173,6 +186,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .GNSentry:
return false
case .Blucon:
return true
}
}
@ -194,6 +209,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .GNSentry:
return ""
case .Blucon:
return "%"
}
}
@ -215,6 +232,8 @@ enum CGMTransmitterType:String, CaseIterable {
case .GNSentry:
return false
case .Blucon:
return false
}
}
}

View File

@ -1,9 +1,167 @@
//
// CGMBluconTransmitter.swift
// xdrip
//
// Created by Johan Degraeve on 13/07/2019.
// Copyright © 2019 Johan Degraeve. All rights reserved.
//
import Foundation
import CoreBluetooth
import os
class CGMBluconTransmitter: BluetoothTransmitter {
// MARK: - properties
/// will be used to pass back bluetooth and cgm related events
private(set) weak var cgmTransmitterDelegate:CGMTransmitterDelegate?
/// Blucon Service
let CBUUID_BluconService = "436A62C0-082E-4CE8-A08B-01D81F195B24"
/// receive characteristic
let CBUUID_ReceiveCharacteristic_Blucon: String = "436A0C82-082E-4CE8-A08B-01D81F195B24"
/// write characteristic
let CBUUID_WriteCharacteristic_Blucon: String = "436AA6E9-082E-4CE8-A08B-01D81F195B24"
/// for OS_log
private let log = OSLog(subsystem: Constants.Log.subSystem, category: Constants.Log.categoryBlucon)
// actual device address
private var actualDeviceAddress:String?
// used in parsing packet
private var timeStampLastBgReading:Date
// waiting successful pairing yes or not
private var waitingSuccessfulPairing:Bool = false
// MARK: - public functions
/// - parameters:
/// - address: if already connected before, then give here the address that was received during previous connect, if not give nil
/// - transmitterID: expected transmitterID
init?(address:String?, transmitterID:String, delegate:CGMTransmitterDelegate, timeStampLastBgReading:Date) {
// 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
}
//initialize timeStampLastBgReading
self.timeStampLastBgReading = timeStampLastBgReading
// initialize
super.init(addressAndName: newAddressAndName, CBUUID_Advertisement: nil, servicesCBUUIDs: [CBUUID(string: CBUUID_BluconService)], CBUUID_ReceiveCharacteristic: CBUUID_ReceiveCharacteristic_Blucon, CBUUID_WriteCharacteristic: CBUUID_WriteCharacteristic_Blucon, startScanningAfterInit: CGMTransmitterType.Blucon.startScanningAfterInit())
//assign CGMTransmitterDelegate
cgmTransmitterDelegate = delegate
// set self as delegate for BluetoothTransmitterDelegate - this parameter is defined in the parent class BluetoothTransmitter
bluetoothTransmitterDelegate = self
}
// MARK: - private helper functions
/// will check if the transmitter id as set by the user is complete, and if not complete it
///
/// user may just add the digits in which case this function will add BLU... with a number of 0's
private static func createExpectedDeviceName(transmitterIdSetByUser: String) -> String {
var returnValue = transmitterIdSetByUser
if !returnValue.uppercased().startsWith("BLU") {
while returnValue.count < 5 {
returnValue = "0" + returnValue;
}
returnValue = "BLU" + returnValue;
}
return returnValue
}
}
extension CGMBluconTransmitter: CGMTransmitter {
func initiatePairing() {
// nothing to do, Blucon keeps on reconnecting, resulting in continous pairing request
return
}
func reset(requested: Bool) {
// no reset supported for blucon
return
}
}
extension CGMBluconTransmitter: BluetoothTransmitterDelegate {
func centralManagerDidConnect(address: String?, name: String?) {
os_log("in centralManagerDidConnect", log: log, type: .info)
cgmTransmitterDelegate?.cgmTransmitterDidConnect(address: address, name: name)
}
func centralManagerDidFailToConnect(error: Error?) {
os_log("in centralManagerDidFailToConnect", log: log, type: .error)
}
func centralManagerDidUpdateState(state: CBManagerState) {
cgmTransmitterDelegate?.deviceDidUpdateBluetoothState(state: state)
}
func centralManagerDidDisconnectPeripheral(error: Error?) {
os_log("in centralManagerDidDisconnectPeripheral", log: log, type: .info)
cgmTransmitterDelegate?.cgmTransmitterDidDisconnect()
}
func peripheralDidUpdateNotificationStateFor(characteristic: CBCharacteristic, error: Error?) {
os_log("in peripheralDidUpdateNotificationStateFor", log: log, type: .info)
// check if error occurred
if let error = error {
// no need to log the error, it's already logged in BluetoothTransmitter
// check if it's an encryption error, if so call delegate
if error.localizedDescription.uppercased().contains(find: "ENCRYPTION IS INSUFFICIENT") {
cgmTransmitterDelegate?.cgmTransmitterNeedsPairing()
waitingSuccessfulPairing = true
}
} else {
if waitingSuccessfulPairing {
cgmTransmitterDelegate?.successfullyPaired()
waitingSuccessfulPairing = false
}
}
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
// log the received characteristic value
os_log("in peripheralDidUpdateValueFor with characteristic UUID = %{public}@", log: log, type: .info, characteristic.uuid.uuidString)
// this is only applicable the very first time that blucon connects and pairing is done
if waitingSuccessfulPairing {
cgmTransmitterDelegate?.successfullyPaired()
waitingSuccessfulPairing = false
}
// check if error occured
if let error = error {
os_log(" error: %{public}@", log: log, type: .error , error.localizedDescription)
}
if let value = characteristic.value {
let data = value.hexEncodedString()
os_log("in peripheral didUpdateValueFor, data = %{public}@", log: log, type: .debug, data)
} else {
os_log("in peripheral didUpdateValueFor, value is nil, no further processing", log: log, type: .error)
}
}
}

View File

@ -138,7 +138,8 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
}
func peripheralDidUpdateValueFor(characteristic: CBCharacteristic, error: Error?) {
// log the receivec characteristic value
// log the received characteristic value
os_log("in peripheralDidUpdateValueFor with characteristic UUID = %{public}@, matches characteristic name %{public}@", log: log, type: .info, characteristic.uuid.uuidString, receivedCharacteristicUUIDToCharacteristic(characteristicUUID: characteristic.uuid.uuidString)?.description ?? "not available")
if let error = error {
@ -323,6 +324,7 @@ class CGMGNSEntryTransmitter:BluetoothTransmitter, BluetoothTransmitterDelegate,
}
}
/// creates readable representation of characteristicUUID, for logging only
private func receivedCharacteristicUUIDToCharacteristic(characteristicUUID:String) -> CBUUID_Characteristic_UUID? {
if CBUUID_Characteristic_UUID.CBUUID_BatteryLevel.rawValue.containsIgnoringCase(find: characteristicUUID) {
return CBUUID_Characteristic_UUID.CBUUID_BatteryLevel

View File

@ -105,6 +105,9 @@ final class RootViewController: UIViewController {
/// reference to bgReadingSpeaker
private var bgReadingSpeaker:BGReadingSpeaker?
/// timestamp of last notification for pairing
private var timeStampLastNotificationForPairing:Date?
// MARK: - View Life Cycle
override func viewWillAppear(_ animated: Bool) {
@ -593,6 +596,12 @@ final class RootViewController: UIViewController {
cgmTransmitter = CGMGNSEntryTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
calibrator = Libre1Calibrator()
case .Blucon:
if let currentTransmitterId = UserDefaults.standard.transmitterId {
cgmTransmitter = CGMBluconTransmitter(address: UserDefaults.standard.bluetoothDeviceAddress, transmitterID: currentTransmitterId, delegate: self, timeStampLastBgReading: Date(timeIntervalSince1970: 0))
calibrator = Libre1Calibrator()
}
}
}
@ -1001,6 +1010,9 @@ extension RootViewController:CGMTransmitterDelegate {
func successfullyPaired() {
// remove existing notification if any
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
// invalidate transmitterPairingResponseTimer
if let transmitterPairingResponseTimer = transmitterPairingResponseTimer {
transmitterPairingResponseTimer.invalidate()
@ -1063,6 +1075,20 @@ extension RootViewController:CGMTransmitterDelegate {
os_log("transmitter needs pairing", log: log, type: .info)
if let timeStampLastNotificationForPairing = timeStampLastNotificationForPairing {
// check timestamp of last notification, if too soon then return
if Int(abs(timeStampLastNotificationForPairing.timeIntervalSinceNow)) < Constants.BluetoothPairing.minimumTimeBetweenTwoPairingNotificationsInSeconds {
return
}
}
// set timeStampLastNotificationForPairing
timeStampLastNotificationForPairing = Date()
// remove existing notification if any
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [Constants.Notifications.NotificationIdentifierForTransmitterNeedsPairing.transmitterNeedsPairing])
// Create Notification Content
let notificationContent = UNMutableNotificationContent()

View File

@ -28,7 +28,7 @@ class SettingsViewDexcomSettingsViewModel:SettingsViewModelProtocol {
if let transmitterType = UserDefaults.standard.transmitterType {
switch transmitterType {
case .dexcomG4, .miaomiao, .GNSentry:
case .dexcomG4, .miaomiao, .GNSentry, .Blucon:
return true
case .dexcomG5, .dexcomG6:

View File

@ -170,11 +170,11 @@ struct SettingsViewTransmitterSettingsViewModel:SettingsViewModelProtocol {
switch transmitterType {
case .dexcomG4, .miaomiao, .GNSentry:
break
case .dexcomG5, .dexcomG6:
UserDefaults.standard.dexcomShareSerialNumber = UserDefaults.standard.transmitterId
default:
break
}
}