548 lines
22 KiB
Swift
548 lines
22 KiB
Swift
//
|
|
// MFFGHSBluetoothPair.swift
|
|
// MobileFirstFramework
|
|
//
|
|
// Created by Chowdhury, Shohrab on 3/29/19.
|
|
// Copyright © 2019 Verizon Wireless. All rights reserved.
|
|
//
|
|
|
|
import CoreBluetooth
|
|
|
|
public enum MFFGHSSignalStatus: NSNumber {
|
|
case noBluetoothConnection = -1, noSignal = 1, poor, good, confirmedGood, confirmedPoor
|
|
}
|
|
|
|
public enum MFFGHSSignalStrengthCheckStatus: NSNumber {
|
|
case idle = 0, processing, complete
|
|
}
|
|
|
|
public protocol MFFGHSBluetoothTestingProtocol {
|
|
var continueTesting: Bool { get set }
|
|
}
|
|
|
|
public protocol MFFGHSBluetoothPairDelegate : NSObjectProtocol {
|
|
|
|
/// Receive ble status ON/OFF
|
|
func bluetoothStatus(isOn: Bool)
|
|
|
|
/// New status that's more reliable as a callback if BLE is off
|
|
func bluetoothOff()
|
|
|
|
/// Bluetooth does not have access from the user
|
|
func bluetoothPermissionsDenied()
|
|
|
|
/// BLE found a device that doesn't match what it expects to find.
|
|
func deviceIMEIMismatch(expected: String, found: String)
|
|
|
|
/// An attempted pairing failed due to a broadcast naming mismatch.
|
|
func onBluetoothDiscoveredNameFailed(expectedName: String, foundNames: [String])
|
|
|
|
/// The bluetooth service was discovered.
|
|
func onBluetoothDiscovered(expectedName: String, foundName: String)
|
|
|
|
/// Check device is paired with phone
|
|
func pairUpdate(isPaired: Bool)
|
|
|
|
/// Check device is activated or not
|
|
func updateActivatedStatus(isActivated: Bool)
|
|
|
|
/// Receive current RSSI
|
|
func updateCurrentSignalStatus(_ status: MFFGHSSignalStatus, strengthCheckStatus: MFFGHSSignalStrengthCheckStatus, rssi: Double?)
|
|
|
|
}
|
|
|
|
extension MFFGHSBluetoothPairDelegate {
|
|
public func bluetoothStatus(isOn: Bool) {}
|
|
public func bluetoothOff() {}
|
|
public func bluetoothPermissionsDenied() {}
|
|
public func deviceIMEIMismatch(expected: String, found: String) {}
|
|
public func onBluetoothDiscoveredNameFailed(expectedName: String, foundNames: [String]) {}
|
|
public func onBluetoothDiscovered(expectedName: String, foundName: String) {}
|
|
public func pairUpdate(isPaired: Bool) {}
|
|
public func updateActivatedStatus(isActivated:Bool) {}
|
|
public func updateCurrentSignalStatus(_ status: MFFGHSSignalStatus, strengthCheckStatus: MFFGHSSignalStrengthCheckStatus, rssi: Double?) {}
|
|
}
|
|
|
|
class MFFGHSBluetoothPair: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate, BluetoothParingProtocol {
|
|
|
|
// !!Major change!! Some controllers are relying on bluetooth object being passed.
|
|
static weak var instance:MFFGHSBluetoothPair?
|
|
|
|
/// Weak instance returned. Controller that uses it should hold onto strong reference. When all controllers deallocate, this will also.
|
|
static var sharedInstance: MFFGHSBluetoothPair {
|
|
get {
|
|
if let instance = instance {
|
|
return instance
|
|
} else {
|
|
let newInstance = MFFGHSBluetoothPair()
|
|
instance = newInstance
|
|
return newInstance
|
|
}
|
|
}
|
|
}
|
|
|
|
// Bluetooth control objects.
|
|
var centralManager: CBCentralManager?
|
|
var peripheral: CBPeripheral?
|
|
|
|
// Event delegate.
|
|
weak var delegate: MFFGHSBluetoothPairDelegate?
|
|
|
|
//debugger for event delegate
|
|
var bluetoothDebugger: BluetoothDebugger?
|
|
|
|
// Data from bluetooth connection
|
|
var isPaired = false
|
|
var isCPEActivated = false
|
|
var currentSignalStatus: MFFGHSSignalStatus = .noBluetoothConnection
|
|
var currentRSSIValue: Double?
|
|
|
|
// Configured properties.
|
|
var bluetoothConfig: BluetoothConfigModel? {
|
|
didSet {
|
|
if (oldValue != bluetoothConfig) { // TODO: Needs deep check for config changes.
|
|
setupBluetoothScanner()
|
|
}
|
|
}
|
|
}
|
|
|
|
var signalStrengthObserveDuration: TimeInterval = 30
|
|
var signalPassingPercentage: Double = 100
|
|
var signalStrengthCheckStatus: MFFGHSSignalStrengthCheckStatus = .idle
|
|
var signalStrengthObserverTimer: Timer?
|
|
var lastSignalStatusTimeStamp: Date?
|
|
var signalStrengthGoodTotalTime: Double = 0
|
|
var signalStrengthFailTotalTime: Double = 0
|
|
|
|
override init() {
|
|
super.init()
|
|
setupBluetoothScanner()
|
|
}
|
|
|
|
#if DEBUG
|
|
deinit {
|
|
print("bluetooth destroy")
|
|
}
|
|
#endif
|
|
|
|
var bluetoothEnabled:Bool {
|
|
#if targetEnvironment(simulator)
|
|
return true
|
|
#else
|
|
return self.centralManager?.state == CBManagerState.poweredOn
|
|
#endif
|
|
}
|
|
|
|
func scanForPeripherals(central:CBCentralManager) {
|
|
let advertiseIds = getBleAdvertiseUUIDs()
|
|
if advertiseIds.count > 0 {
|
|
central.scanForPeripherals(withServices: advertiseIds, options: nil)
|
|
} else {
|
|
central.stopScan()
|
|
}
|
|
}
|
|
|
|
func setupBluetoothScanner(showPowerAlert:Bool = false) {
|
|
// Reset connection on new config.
|
|
if let peripheral = self.peripheral {
|
|
centralManager?.cancelPeripheralConnection(peripheral)
|
|
self.peripheral = nil
|
|
self.isPaired = false
|
|
self.currentSignalStatus = .noBluetoothConnection
|
|
}
|
|
if self.centralManager == nil || showPowerAlert {
|
|
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main, options: [CBCentralManagerOptionShowPowerAlertKey: showPowerAlert])
|
|
} else if (bluetoothEnabled) {
|
|
scanForPeripherals(central: centralManager!)
|
|
} // else wait for powered on status
|
|
|
|
if GMFGTestScreenData.shared.isCPESimulated {
|
|
#if DEBUG
|
|
simulateBluetoothMessaging()
|
|
#endif
|
|
} else {
|
|
#if DEBUG && targetEnvironment(simulator)
|
|
simulateBluetoothMessaging()
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
// For simulating RSSI changes.
|
|
var timer: Timer?
|
|
private func simulateBluetoothMessaging() {
|
|
// If we don't get advertisement data, technically nothing will come.
|
|
if getBleAdvertiseUUIDs().count == 0 { return }
|
|
|
|
if timer != nil { return }
|
|
|
|
var signalRotate = 0
|
|
_ = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { (timer) in
|
|
self.delegate?.bluetoothStatus(isOn: true)
|
|
self.isCPEActivated = true
|
|
self.delegate?.updateActivatedStatus(isActivated: self.isCPEActivated)
|
|
self.isPaired = true
|
|
self.delegate?.pairUpdate(isPaired: self.isPaired)
|
|
signalRotate = (signalRotate + 1) % 5
|
|
self.currentRSSIValue = Double(signalRotate)
|
|
if (signalRotate >= 3) {
|
|
self.currentRSSIValue = -76
|
|
self.currentSignalStatus = .confirmedGood
|
|
} else if (signalRotate >= 2) {
|
|
self.currentRSSIValue = -86
|
|
self.currentSignalStatus = .good
|
|
} else if (signalRotate >= 1) {
|
|
self.currentRSSIValue = -101
|
|
self.currentSignalStatus = .poor
|
|
} else {
|
|
self.currentRSSIValue = -999
|
|
self.currentSignalStatus = .noSignal
|
|
}
|
|
|
|
self.delegate?.updateCurrentSignalStatus(self.currentSignalStatus, strengthCheckStatus:self.signalStrengthCheckStatus, rssi: self.currentRSSIValue)
|
|
self.bluetoothDebugger?.bluetoothStatus = true
|
|
self.bluetoothDebugger?.isDeviceActivated = true
|
|
self.bluetoothDebugger?.isDevicePaired = self.isPaired
|
|
self.bluetoothDebugger?.updateCurrentSignalStatus(self.currentSignalStatus, strengthCheckStatus:self.signalStrengthCheckStatus, rssi: self.currentRSSIValue)
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Should be used when we detect scanning is no longer required.
|
|
private func stopScanning() {
|
|
if let peripheral = self.peripheral {
|
|
centralManager?.cancelPeripheralConnection(peripheral)
|
|
self.peripheral = nil;
|
|
}
|
|
centralManager?.stopScan()
|
|
centralManager = nil
|
|
}
|
|
|
|
open func shutdown() {
|
|
stopScanning()
|
|
MFFGHSBluetoothPair.instance = nil
|
|
}
|
|
|
|
public func stopNotify5G(controller: MFFGHSBluetoothPairDelegate) {
|
|
if let del = delegate, del === controller {
|
|
delegate = nil
|
|
}
|
|
resetSignalStrengthCheckValue()
|
|
}
|
|
|
|
public func setup(config: BluetoothConfigModel, delegate: BluetoothDebuggerDelegate) {
|
|
bluetoothDebugger = BluetoothDebugger(config: config, delegate: delegate)
|
|
bluetoothConfig = config
|
|
}
|
|
|
|
public func startNotify5G(controller: MFFGHSBluetoothPairDelegate) {
|
|
delegate = controller
|
|
// Initialize
|
|
delegate?.pairUpdate(isPaired: isPaired)
|
|
delegate?.updateActivatedStatus(isActivated: isCPEActivated)
|
|
delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
|
|
if let state = centralManager?.state {
|
|
bluetoothDebugger?.bluetoothState = state
|
|
}
|
|
bluetoothDebugger?.bluetoothStatus = bluetoothEnabled
|
|
bluetoothDebugger?.isDevicePaired = isPaired
|
|
bluetoothDebugger?.isDeviceActivated = isCPEActivated
|
|
bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus:signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
}
|
|
|
|
// MARK:- Bluetooth delegate
|
|
|
|
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
bluetoothDebugger?.bluetoothState = central.state
|
|
switch (central.state) {
|
|
case .poweredOn:
|
|
delegate?.bluetoothStatus(isOn: true)
|
|
bluetoothDebugger?.bluetoothStatus = true
|
|
scanForPeripherals(central: central)
|
|
case .poweredOff, .unsupported, .unauthorized, .unknown:
|
|
delegate?.bluetoothStatus(isOn: false)
|
|
bluetoothDebugger?.bluetoothStatus = false
|
|
default:
|
|
print("bluetooth not powered on")
|
|
}
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
|
|
if findPeripheralName(getBleAdvertiseName(), in: advertisementData) {
|
|
self.peripheral = peripheral
|
|
peripheral.delegate = self
|
|
central.connect(peripheral, options: nil)
|
|
central.stopScan()
|
|
} else {
|
|
if let foundName = advertisementData[CBAdvertisementDataLocalNameKey] as? String, foundName.hasPrefix(GMFGConstant.BLE.advertisePrefix) {
|
|
delegate?.deviceIMEIMismatch(expected: getBleAdvertiseName(), found: foundName)
|
|
bluetoothDebugger?.addDeviceIMEIMismatch(found: foundName)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func findPeripheralName(_ name: String, in advertisementData: [String : Any]) -> Bool {
|
|
var found = false
|
|
var foundAdvertisedNames: [String] = []
|
|
|
|
// Check the local data name. (Where it should be. Used for testing app.)
|
|
if let localServiceName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
|
|
if localServiceName == name {
|
|
found = true
|
|
} else {
|
|
foundAdvertisedNames.append(localServiceName)
|
|
}
|
|
}
|
|
// Check the service data. (Where it is actually implemented by CPE.)
|
|
if !found, let avdService = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID:Data] {
|
|
for key in avdService.keys {
|
|
if let rawName = avdService[key],
|
|
let advertisedName = String(bytes: rawName, encoding: .utf8) {
|
|
if (advertisedName == name) {
|
|
found = true
|
|
break
|
|
} else {
|
|
foundAdvertisedNames.append(advertisedName)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if found {
|
|
// Currently exact equality passes. Later can adjust to be actual advertised name if the requirement relaxes.
|
|
delegate?.onBluetoothDiscovered(expectedName: name, foundName: name)
|
|
bluetoothDebugger?.deviceDiscovered = name
|
|
let value: [String : Any] = [
|
|
"blePairStatus": "discovered",
|
|
"expectedBroadcastName": name,
|
|
"foundBroadcastName": name
|
|
]
|
|
track(value)
|
|
|
|
return true;
|
|
} else {
|
|
delegate?.onBluetoothDiscoveredNameFailed(expectedName: name, foundNames: foundAdvertisedNames)
|
|
bluetoothDebugger?.addDiscoveredNameFailures(foundNames: foundAdvertisedNames)
|
|
let value: [String : Any] = [
|
|
"blePairStatus": "mismatch",
|
|
"expectedBroadcastName": name,
|
|
"foundBroadcastName": foundAdvertisedNames.joined(separator: " / ").prefix(500)
|
|
]
|
|
track(value)
|
|
|
|
return false
|
|
}
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
|
|
isPaired = true
|
|
delegate?.pairUpdate(isPaired: true)
|
|
bluetoothDebugger?.isDevicePaired = true
|
|
peripheral.discoverServices(getBleAdvertiseUUIDs()) // For testing app which can only advertise on 1 service.
|
|
peripheral.discoverServices(getSeviceUUIDs())
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
|
|
for service in peripheral.services ?? [] {
|
|
peripheral.discoverCharacteristics(getCharacteristicUUIDs(serviceId: service.uuid), for: service)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
|
|
for characteristic in service.characteristics ?? [] {
|
|
peripheral.setNotifyValue(true, for: characteristic)
|
|
}
|
|
}
|
|
|
|
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
|
|
|
|
let cpeResponse = formatCharacteristicData(characteristic)
|
|
|
|
handleActivated(response: cpeResponse)
|
|
|
|
handleFiveGSignal(response: cpeResponse)
|
|
}
|
|
|
|
public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) { }
|
|
|
|
public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
|
|
// We only connect to 1 peripheral. Using that assumption below.
|
|
isPaired = false
|
|
currentSignalStatus = .noBluetoothConnection
|
|
currentRSSIValue = nil
|
|
delegate?.pairUpdate(isPaired: false)
|
|
delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
bluetoothDebugger?.isDevicePaired = false
|
|
bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
self.peripheral = nil
|
|
scanForPeripherals(central: central)
|
|
}
|
|
|
|
func formatCharacteristicData(_ characteristic: CBCharacteristic) -> [String: Any] {
|
|
guard
|
|
let advertiseData = characteristic.value, !advertiseData.isEmpty,
|
|
let advertiseDict = try? JSONSerialization.jsonObject(with: advertiseData, options: []) as? [String: Any]
|
|
else {
|
|
return [:]
|
|
}
|
|
return advertiseDict
|
|
}
|
|
|
|
// MARK: Handle Activation
|
|
func handleActivated(response: [String: Any]) {
|
|
if let activation = response["Activation"] as? [String: Any] {
|
|
isCPEActivated = activation.stringForkey("Status") == "Activated"
|
|
delegate?.updateActivatedStatus(isActivated: isCPEActivated)
|
|
bluetoothDebugger?.isDeviceActivated = isCPEActivated
|
|
}
|
|
}
|
|
|
|
// MARK: Handle FiveGSignal
|
|
func handleFiveGSignal(response: [String: Any]) {
|
|
if let fivegSignal = response["5GSignal"] as? [String: Any] {
|
|
if let rssiValue = parseRSRPFromJSON(fiveGData: fivegSignal) {
|
|
currentRSSIValue = rssiValue
|
|
|
|
// This need to call before currentSignalStatus set with new status. This is to gather the amount of time on the previous signal status before the update.
|
|
if signalStrengthCheckStatus == .processing {
|
|
//lastSignalStatusTimeStamp = lastSignalStatusTimeStamp != nil ? lastSignalStatusTimeStamp : Date()
|
|
checkSignalStrengthContinueRequired()
|
|
}
|
|
|
|
if GMFGTestScreenData.shared.isEnable5GSignal {
|
|
currentSignalStatus = currentSignalStatus == .confirmedGood ? .confirmedGood : .good
|
|
} else {
|
|
if rssiValue >= higherThreshold || rssiValue <= lowerThreshold {
|
|
// Future reference: -999 means no signal from CPE. Need to make sure it will always be included in check.
|
|
// Invalid value. We should not be above the upper limits or beneath the lower limits.
|
|
currentSignalStatus = .noSignal
|
|
} else if rssiValue >= rssiThreshold {
|
|
currentSignalStatus = currentSignalStatus == .confirmedGood ? .confirmedGood : .good
|
|
} else {
|
|
currentSignalStatus = .poor
|
|
}
|
|
}
|
|
} else {
|
|
currentSignalStatus = .noSignal // Invalid value or doesn't exist.
|
|
}
|
|
|
|
delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
}
|
|
}
|
|
|
|
// MARK:- Handle Signal History
|
|
|
|
private func resetSignalStrengthCheckValue() {
|
|
signalStrengthCheckStatus = .idle
|
|
signalStrengthObserverTimer?.invalidate()
|
|
signalStrengthObserverTimer = nil
|
|
lastSignalStatusTimeStamp = nil
|
|
signalStrengthGoodTotalTime = 0
|
|
signalStrengthFailTotalTime = 0
|
|
}
|
|
|
|
func checkSignalStrengthContinueRequired() {
|
|
let lastTrackDate = lastSignalStatusTimeStamp ?? Date()
|
|
if [.good, .confirmedGood].contains(currentSignalStatus) {
|
|
signalStrengthGoodTotalTime += Date().timeIntervalSince(lastTrackDate)
|
|
} else {
|
|
signalStrengthFailTotalTime += Date().timeIntervalSince(lastTrackDate)
|
|
}
|
|
lastSignalStatusTimeStamp = Date()
|
|
evaluateSignalStrength()
|
|
}
|
|
|
|
private func evaluateSignalStrength() {
|
|
let maxFailPercentage = 100 - signalPassingPercentage
|
|
let maxFailTime = (maxFailPercentage / 100) * signalStrengthObserveDuration
|
|
let maxPassTime = (signalPassingPercentage / 100) * signalStrengthObserveDuration
|
|
|
|
#if DEBUG
|
|
print("[ MFFGHSBluetoothPair ] Good Signal Time >>>> \(signalStrengthGoodTotalTime)")
|
|
print("[ MFFGHSBluetoothPair ] Bad Signal Time >>>> \(signalStrengthFailTotalTime)")
|
|
#endif
|
|
|
|
if signalStrengthFailTotalTime > maxFailTime {
|
|
// already Fail, stop testing
|
|
resetSignalStrengthCheckValue()
|
|
signalStrengthCheckStatus = .complete
|
|
currentSignalStatus = .confirmedPoor
|
|
delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
} else if signalStrengthGoodTotalTime >= maxPassTime || signalStrengthCheckStatus == .complete {
|
|
// already PASS. stop testing and handle
|
|
resetSignalStrengthCheckValue()
|
|
signalStrengthCheckStatus = .complete
|
|
currentSignalStatus = .confirmedGood
|
|
delegate?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
bluetoothDebugger?.updateCurrentSignalStatus(currentSignalStatus, strengthCheckStatus: signalStrengthCheckStatus, rssi: currentRSSIValue)
|
|
} else {
|
|
//contiue
|
|
}
|
|
}
|
|
|
|
public func startSignalStrengthCheckValue(duration: TimeInterval, percentage: Double){
|
|
if signalStrengthCheckStatus != .processing {
|
|
resetSignalStrengthCheckValue()
|
|
signalStrengthObserveDuration = duration
|
|
signalPassingPercentage = percentage
|
|
if currentSignalStatus == .confirmedGood {
|
|
currentSignalStatus = .good
|
|
}
|
|
signalStrengthCheckStatus = .processing
|
|
lastSignalStatusTimeStamp = Date()
|
|
signalStrengthObserverTimer = Timer(timeInterval: (signalStrengthObserveDuration + 0.3), repeats: false) { (timer) in
|
|
#if DEBUG
|
|
print("[ MFFGHSBluetoothPair ] Signal Test Timeout")
|
|
#endif
|
|
self.signalStrengthCheckStatus = .complete
|
|
self.checkSignalStrengthContinueRequired()
|
|
}
|
|
RunLoop.main.add(signalStrengthObserverTimer!, forMode: .common)
|
|
}
|
|
}
|
|
|
|
// MARK:- Utility Functions
|
|
func parseRSRPFromJSON(fiveGData: [String: Any]) -> Double? {
|
|
return fiveGData["SS-RSRP"] as? Double ?? nil
|
|
}
|
|
|
|
// MARK:- Analytics
|
|
func track(_ data: [String: Any]) {
|
|
if let _delegate = delegate {
|
|
trackGemini(value: data.merging(["delegateName": String(describing: _delegate)]) { $1 }, logType: .BLE)
|
|
}
|
|
}
|
|
|
|
// MARK: BluetoothConfigModel methods
|
|
var rssiThreshold: Double {
|
|
return bluetoothConfig?.bleSignalThreshold ?? 0
|
|
}
|
|
|
|
var lowerThreshold: Double {
|
|
return bluetoothConfig?.bleSignalLowerBound ?? 0
|
|
}
|
|
|
|
var higherThreshold: Double {
|
|
return bluetoothConfig?.bleSignalUpperBound ?? 0
|
|
}
|
|
|
|
func getBleAdvertiseUUIDs() -> [CBUUID] {
|
|
return bluetoothConfig?.peripherals.map{ $0.uuid } ?? []
|
|
}
|
|
|
|
open func getBleAdvertiseName() -> String {
|
|
return bluetoothConfig?.advertisedData ?? ""
|
|
}
|
|
|
|
func getSeviceUUIDs() -> [CBUUID] {
|
|
return bluetoothConfig?.services.map { $0.uuid } ?? []
|
|
}
|
|
|
|
func getCharacteristicUUIDs(serviceId: CBUUID) -> [CBUUID] {
|
|
return bluetoothConfig?.services.first?.characteristics.map { $0.uuid } ?? []
|
|
}
|
|
}
|