This is a library to use DANAVI Bluetooth Manager API for Swift. By using this library, your app can easily communicate with DANAVI compatible devices using Bluetooth.
This library is distributed through Swift Package Manager. To integrate this library to your app, do the following steps,
Add Bluetooth permission NSBluetoothAlwaysUsageDescription to Info.plist
public struct EmittedEvent {
public var mac: String = "";
public var action: String = "";
public var deviceType: String = "";
public var value: Any = "";
}
public struct Temperature {
public var temperature: Double = 0.0;
public var unit: String = "fahrenheit";
}
public struct OximeterMeasurement {
public var bloodOxygen: Int = 0;
public var heartRate: Int = 0;
public var pi: Int = 0;
}
public struct SpirometerMeasurement {
public var fev1: Double = 0.0;
public var pef: Double = 0.0;
}
public func scanDevices(deviceType type: String, timeout: Double? = 10)
BluetoothManager
will scan for devices with deviceType
for timeout
seconds. The default value for timeout
is 10.0 seconds. After timeout
seconds, the BluetoothManager
will send EmittedEvent(mac: "", action: "ACTION_SCAN_FINISHED", deviceType: "", value: "")
to indicate that the BluetoothManager
has stopped scanning for devices.
When device is found, it will send EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_DEVICE_DISCOVERED", deviceType: "DANAVI Oximeter", value: "")
.
public func stopScan()
Calling this function will send EmittedEvent(mac: "", action: "ACTION_SCAN_FINISHED", deviceType: "", value: "")
to indicate that the BluetoothManager
has stopped scanning for devices.
public func connectDevice(mac: String)
Connect to device with MAC Address mac
. After a successful connection, it will send EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_DEVICE_CONNECTED", deviceType: "DANAVI Oximeter", value: "")
public func disconnectDevice(mac: String)
Cancel connection to device with MAC Address mac
. After a device is disconnected, it will send EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_DEVICE_DISCONNECTED", deviceType: "DANAVI Oximeter", value: "")
public func startDanaviThermometerMeasurement()
Call this function to start thermometer measurement. Please wait for EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT", deviceType: "DANAVI Oximeter", value: "")
, then prompt the user to take temperature measurement using the thermometer.
On successful measurement, it will send EmittedEvent(mac: "5B5BE370-CBB2-0192-0CC0-BC8DC74D4C80", action: "ACTION_DANAVI_THERMOMETER_MEASUREMENT", deviceType: "DANAVI Ear Thermometer", value: DanaviBluetoothManager.Temperature(temperature: 98.2, unit: "fahrenheit"))
public func startDanaviOximeterMeasurement(writeToFile: Bool? = false, withTimer: Bool? = false, measurementLength: Int? = 300, thinningInterval: Int? = 20)
Call this function to start oximeter measurement. Please wait for EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT", deviceType: "DANAVI Oximeter", value: "")
, then prompt the user to take oximeter measurement.
On successful measurement, it will send EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_OXIMETER_LIVE_DATA", deviceType: "DANAVI Oximeter", value: DanaviBluetoothManager.OximeterMeasurement(bloodOxygen: 99, heartRate: 82, pi: 5))
Setting this parameter to true
will write the oximeter measurement to a temporary file with format Blood Oxygen Level, Heart Rate, Pi\n
. Default value is false
. For example, the temporary file will look like this,
99,86,15
99,87,5
99,87,5
Setting this parameter to true
will set the measurement length for oximeter measurement. Default value is false
. After some measurementLength
seconds, it will stop sending the oximeter measurement event and stop writing oximeter measurement data to a temporary file. It will send the remaining measurement length in EmittedEvent(mac: "DANAVI Oximeter", action: "ACTION_OXIMETER_TIMER", deviceType: "DANAVI Oximeter", value: 10)
. When the timer hits 0, it will send EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_OXIMETER_MEASUREMENT_DONE", deviceType: "DANAVI Oximeter", value: "")
to indicate that the oximeter measurement has finished.
Measurement length for oximeter measurement in seconds. Default value is 300 seconds. If withTimer
is false
, then this parameter will not affect anything.
thinningInterval
is used for thinning the received oximeter measurement data. By default, the oximeter will send approximately 20 data/second. Default value is 20, which means that it only takes every 20th measurement (approximately 1 data/second).
public func stopDanaviOximeterMeasurement()
Call this function to stop sending oximeter measurement. It will send EmittedEvent(mac: "49319DAB-B10E-A065-537C-F7E4AC91121A", action: "ACTION_OXIMETER_MEASUREMENT_DONE", deviceType: "DANAVI Oximeter", value: "")
to indicate that the oximeter measurement has finished.
public func retrieveOximeterData() -> String
Call this function to retrieve oximeter data from the temporary file. In order for this to work, must set writeToFile
parameter in startDanaviOximeterMeasurement
function to true
. On failure, it will return -1,-1,-1
.
public func startDanaviSpirometerMeasurement()
Call this function to start spirometer measurement. Please wait for EmittedEvent(mac: "0759B832-91A5-FDE8-C6D3-0B5552DA5B46", action: "ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT", deviceType: "DANAVI Spirometer", value: "")
then prompt the user to take measurement. After successful measurement, you will hear 1 beep from the spirometer device and the measurement will show up on the spirometer's screen. It will take a few seconds (~5 seconds) until the measurement is sent to the app.
On successful measurement, it will send EmittedEvent(mac: "0759B832-91A5-FDE8-C6D3-0B5552DA5B46", action: "ACTION_SPIROMETER_MEASUREMENT", deviceType: "DANAVI Spirometer", value: DanaviBluetoothManager.SpirometerMeasurement(fev1: 2.2600000000000002, pef: 361.0))
. PEF is in L/min and FEV1 is in L.
import DanaviBluetoothManager
var bluetoothManager = BluetoothManager()
Since most of the functions are asynchronous, we use EmittedEvent
struct to communicate between the UIViewController and the BluetoothManager. Our API sends all type of events to EVENT_BLUETOOTH_DANAVI
. In the EmittedEvent
struct, there is an action
field to indicate what type of event that you receive.
override func viewDidLoad() {
super.viewDidLoad()
bluetoothManager.events.listenTo(eventName: EVENT_BLUETOOTH_DANAVI, action: self.onReceiveEvent)
}
func onReceiveEvent(information: Any?) {
let info = information as! EmittedEvent
print("\n\(info)")
if (info.action == ACTION_BLUETOOTH_STATE_CHANGE) {
onBluetoothStateChange(event: info)
} else if (info.action == ACTION_DEVICE_DISCOVERED) {
onDeviceDiscovered(event: info)
} else if (info.action == ACTION_SCAN_FINISHED) {
onScanFinished(event: info)
} else if (info.action == ACTION_DEVICE_CONNECTED) {
onDeviceConnected(event: info)
} else if (info.action == ACTION_DEVICE_DISCONNECTED) {
onDeviceDisconnected(event: info)
} else if (info.action == ACTION_DEVICE_FAILED_TO_CONNECT) {
onDeviceFailedToConnect(event: info)
} else if (info.action == ACTION_DANAVI_THERMOMETER_MEASUREMENT) {
handleReceiveTemperatureMeasurement(event: info)
} else if (info.action == ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT) {
handleReadyTakeMeasurement()
} else if (info.action == ACTION_OXIMETER_LIVE_DATA) {
handleReceiveOximeterMeasurement(event: info)
} else if (info.action == ACTION_OXIMETER_TIMER) {
handleOximeterTimer(event: info)
} else if (info.action == ACTION_OXIMETER_MEASUREMENT_DONE) {
handleOximeterMeasurementDone()
}
}
If the event.value
is equal to PoweredOn
, it means that the phone's Bluetooth state is on and we can use BluetoothManager's APIs. If event.value
is not equal to PoweredOn
, then there is a problem with the phone's Bluetooth. Possible values are Unknown
, Resetting
, Unsupported
, Unauthorized
, PoweredOff
, PoweredOn
, and Error
. The event
structure will look like this,
EmittedEvent(mac: "", action: "ACTION_BLUETOOTH_STATE_CHANGE", deviceType: "", value: "PoweredOn")
func onBluetoothStateChange(event: EmittedEvent) {
if (event.value as! String == "PoweredOn") {
// IF BLUETOOTH STATE IS POWERED ON, THEN READY TO SCAN DEVICES
bluetoothManager.scanDevices(deviceType: DANAVI_THERMOMETER_TYPE, timeout: 3.0)
} else {
// NEED TO HANDLE -- BLUETOOTH STATE IS NOT ON
}
}
Start scanning for nearby devices by using scanDevices
function,
Sample EmittedEvent
EmittedEvent(mac: "EDC8D22F-CEFF-F0E3-76A7-D690CD8F09C8", action: "ACTION_DEVICE_DISCOVERED", deviceType: "TS28B", value: "")
EmittedEvent(mac: "", action: "ACTION_SCAN_FINISHED", deviceType: "", value: "")
Initiate a connection to device by using connectDevice
function.
On successful connection,
EmittedEvent(mac: "EDC8D22F-CEFF-F0E3-76A7-D690CD8F09C8", action: "ACTION_DEVICE_CONNECTED", deviceType: "TS28B", value: "")
On failed connection,
EmittedEvent(mac: "EDC8D22F-CEFF-F0E3-76A7-D690CD8F09C8", action: "ACTION_DEVICE_FAILED_TO_CONNECT", deviceType: "TS28B", value: "")
Once the device is connected to the phone, then can start listening for measurement event by using startDanaviThermometerMeasurement
function. Wait until the app receives EmittedEvent
with ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT
action
, then prompt the user to use the thermometer to take measurement.
Notification event
EmittedEvent(mac: "EDC8D22F-CEFF-F0E3-76A7-D690CD8F09C8", action: "ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT", deviceType: "TS28B", value: "")
After the user use the thermometer, it will generate EmittedEvent
that looks like this,
EmittedEvent(mac: "EDC8D22F-CEFF-F0E3-76A7-D690CD8F09C8", action: "ACTION_DANAVI_THERMOMETER_MEASUREMENT", deviceType: "TS28B", value: DanaviBluetoothManager.Temperature(temperature: 98.2, unit: "fahrenheit"))
After receiving measurement, do a cleanup by disconnectDevice(mac)
and bluetoothManager.events.removeListeners(eventNameToRemoveOrNil: EVENT_BLUETOOTH_DANAVI)
//
// ViewController.swift
// BluetoothTest2
//
// Created by Darren Muliawan on 8/26/20.
// Copyright © 2020 Darren Muliawan. All rights reserved.
//
import UIKit
import CoreBluetooth
import DanaviBluetoothManager
class ViewController: UIViewController {
var bluetoothManager = BluetoothManager()
var discoveredMac: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
bluetoothManager.events.listenTo(eventName: EVENT_BLUETOOTH_DANAVI, action: self.onReceiveEvent)
bluetoothManager.enableBluetoothLogging()
}
// WRAPPER FUNCTION TO HANDLE ALL EVENTS SENT FROM BLUETOOTH MANAGER
func onReceiveEvent(information: Any?) {
let info = information as! EmittedEvent
if (info.action == ACTION_BLUETOOTH_STATE_CHANGE) {
print("\n\(info)")
onBluetoothStateChange(event: info)
} else if (info.action == ACTION_DEVICE_DISCOVERED) {
print("\n\(info)")
onDeviceDiscovered(event: info)
} else if (info.action == ACTION_SCAN_FINISHED) {
print("\n\(info)")
onScanFinished(event: info)
} else if (info.action == ACTION_DEVICE_CONNECTED) {
print("\n\(info)")
onDeviceConnected(event: info)
} else if (info.action == ACTION_DEVICE_DISCONNECTED) {
print("\n\(info)")
onDeviceDisconnected(event: info)
} else if (info.action == ACTION_DEVICE_FAILED_TO_CONNECT) {
print("\n\(info)")
onDeviceFailedToConnect(event: info)
} else if (info.action == ACTION_DANAVI_THERMOMETER_MEASUREMENT) {
print("\n\(info)")
handleReceiveTemperatureMeasurement(event: info)
} else if (info.action == ACTION_DEVICE_READY_TO_TAKE_MEASUREMENT) {
print("\n\(info)")
handleReadyTakeMeasurement()
} else if (info.action == ACTION_OXIMETER_LIVE_DATA) {
print("\n\(info)")
handleReceiveOximeterMeasurement(event: info)
} else if (info.action == ACTION_OXIMETER_TIMER) {
//print("\n\(info)")
handleOximeterTimer(event: info)
} else if (info.action == ACTION_OXIMETER_MEASUREMENT_DONE) {
print("\n\(info)")
handleOximeterMeasurementDone()
} else if (info.action == ACTION_DANAVI_SPIROMETER_MEASUREMENT_TAKEN) {
print("\n\(info)")
handleSpirometerMeasurementTaken(event: info)
} else if (info.action == ACTION_SPIROMETER_MEASUREMENT) {
print("\n\(info)")
handleSpirometerMeasurement(event: info)
}
}
// FUNCTION TO HANDLE BLUETOOTH STATE CHANGE
func onBluetoothStateChange(event: EmittedEvent) {
if (event.value as! String == "PoweredOn") {
// IF BLUETOOTH STATE IS POWERED ON, THEN READY TO SCAN DEVICES
//bluetoothManager.scanDevices(deviceType: DANAVI_EAR_THERMOMETER_TYPE, timeout: 5.0)
bluetoothManager.scanDevices(deviceType: DANAVI_SPIROMETER_TYPE, timeout: 5.0)
} else {
// NEED TO HANDLE -- BLUETOOTH STATE IS NOT ON
}
}
func onDeviceDiscovered(event: EmittedEvent) {
// IF DEVICE IS DISCOVERED, APPEND MAC ADDRESS TO ARRAY
self.discoveredMac.append(event.mac)
}
func onScanFinished(event: EmittedEvent) {
// BLUETOOTH SCANNING HAS FINISHED
if (self.discoveredMac.count == 1) {
// ONLY 1 DEVICE FOUND, ATTEMPT TO CONNECT RIGHT AWAY
bluetoothManager.connectDevice(mac: self.discoveredMac[0])
} else if (self.discoveredMac.count == 0) {
// NEED TO HANDLE -- NO DEVICE FOUND
} else {
// IF MORE THAN 1 DEVICE FOUND, PROMPT USER TO CHOOSE DEVICE
}
}
func onDeviceConnected(event: EmittedEvent) {
// IF DEVICE IS CONNECTED, THEN START MEASUREMENT
if (event.deviceType == DANAVI_EAR_THERMOMETER_TYPE) {
bluetoothManager.startDanaviThermometerMeasurement()
//bluetoothManager.disconnectDevice(mac: event.mac)
} else if (event.deviceType == DANAVI_OXIMETER_TYPE) {
bluetoothManager.startDanaviOximeterMeasurement(writeToFile: true, withTimer: true, measurementLength: 10, thinningInterval: 20)
} else if (event.deviceType == DANAVI_SPIROMETER_TYPE) {
bluetoothManager.startDanaviSpirometerMeasurement()
}
}
func onDeviceDisconnected(event: EmittedEvent) {
// TODO: UPDATE UI
}
func onDeviceFailedToConnect(event: EmittedEvent) {
// TODO: UPDATE UI
}
func handleReceiveTemperatureMeasurement(event: EmittedEvent) {
// GET TEMPERATURE OBJECT
let measurement = event.value as! Temperature
print("Temperature: \(measurement.temperature) \(measurement.unit)")
// UPDATE UI
}
func handleReceiveOximeterMeasurement(event: EmittedEvent) {
// GET OXIMETER MEASUREMENT OBJECT
let measurement = event.value as! OximeterMeasurement
print("Blood Oxygen: \(measurement.bloodOxygen), Heart Rate: \(measurement.heartRate), Pi: \(measurement.pi)")
// UPDATE UI
}
func handleReadyTakeMeasurement() {
// UPDATE UI TO PROMPT USER TO USE DEVICE
}
func handleOximeterTimer(event: EmittedEvent) {
// UPDATE UI TO SHOW THE OXIMETER MEASUREMENT REMAINING TIME
let remainingTime = event.value
print(remainingTime)
}
func handleOximeterMeasurementDone() {
// UPDATE UI
// PROMPT USER THAT THEY HAVE FINISHED THE MEASUREMENT
let data = bluetoothManager.retrieveOximeterData().split(whereSeparator: \.isNewline)
print("Total recorded measurement: \(data.count)\n\(data)")
}
func handleSpirometerMeasurementTaken(event: EmittedEvent) {
// UPDATE UI
}
func handleSpirometerMeasurement(event: EmittedEvent) {
// UPDATE UI
let measurement = event.value as! SpirometerMeasurement
print("FEV1: \(measurement.fev1), PEF: \(measurement.pef)")
}
}
link |
Stars: 0 |
Last commit: 2 years ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics