Completed the oxymeter category

dev_3.3_BLE
Faiz Hashmi 2 years ago
parent 0a700270f4
commit 70ca4071fc

@ -10,13 +10,13 @@ import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel
import android.util.SparseArray import android.util.SparseArray
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.cloud.diplomaticquarterapp.ble.OxymeterModel
import com.cloud.diplomaticquarterapp.ble.utils.EcgData import com.cloud.diplomaticquarterapp.ble.utils.EcgData
import com.google.gson.Gson import com.google.gson.Gson
//Ble //Ble
import com.jeremyliao.liveeventbus.LiveEventBus import com.jeremyliao.liveeventbus.LiveEventBus
import com.lepu.blepro.constants.Constant
import com.lepu.blepro.event.EventMsgConst import com.lepu.blepro.event.EventMsgConst
import com.lepu.blepro.event.EventMsgConst.Ble.* import com.lepu.blepro.event.EventMsgConst.Ble.*
import com.lepu.blepro.event.InterfaceEvent import com.lepu.blepro.event.InterfaceEvent
@ -31,15 +31,11 @@ import com.lepu.blepro.ext.er2.Er2File
import com.lepu.blepro.ext.er2.RtData import com.lepu.blepro.ext.er2.RtData
import com.lepu.blepro.ext.bp2.* import com.lepu.blepro.ext.bp2.*
import com.lepu.blepro.ext.pc60fw.RtParam import com.lepu.blepro.ext.pc60fw.RtParam
import com.lepu.blepro.ext.sp20.GetConfigResult
import com.lepu.blepro.ext.sp20.RtWave import com.lepu.blepro.ext.sp20.RtWave
import com.lepu.blepro.ext.sp20.SetConfigResult
import com.lepu.blepro.ext.sp20.TempResult
import com.lepu.blepro.objs.Bluetooth import com.lepu.blepro.objs.Bluetooth
import com.lepu.blepro.objs.BluetoothController import com.lepu.blepro.objs.BluetoothController
import com.lepu.blepro.utils.DateUtil import com.lepu.blepro.utils.DateUtil
import com.lepu.blepro.utils.Er1Decompress import com.lepu.blepro.utils.Er1Decompress
import kotlin.reflect.KFunction2
class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) { class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivity: MainActivity) {
@ -58,6 +54,7 @@ class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivi
private const val CHANNEL = "BLE-Platform-Bridge" private const val CHANNEL = "BLE-Platform-Bridge"
private const val EVENTCHANNEL = "BLE-Platform-Bridge-Event" private const val EVENTCHANNEL = "BLE-Platform-Bridge-Event"
private const val SCAN_DEVICE = "scan" private const val SCAN_DEVICE = "scan"
private const val CONNECT_DEVICE = "connectDevice"
private const val SCAN_DEVICE_EKG = "scan_ekg" private const val SCAN_DEVICE_EKG = "scan_ekg"
private const val EKG_FILES_LIST = "ekg_files_list" private const val EKG_FILES_LIST = "ekg_files_list"
private const val BP2_FILES_LIST = "bp2_files_list" private const val BP2_FILES_LIST = "bp2_files_list"
@ -171,7 +168,9 @@ class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivi
channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result -> channel.setMethodCallHandler { methodCall: MethodCall, result: MethodChannel.Result ->
if (methodCall.method == SCAN_DEVICE) { if (methodCall.method == SCAN_DEVICE) {
scanDevice(methodCall, result) scanDevice()
} else if (methodCall.method == CONNECT_DEVICE) {
connectDevice(methodCall.arguments as List<String>)
} else if (methodCall.method == DISCONNECT_DEVICE) { } else if (methodCall.method == DISCONNECT_DEVICE) {
disconnectDevice(methodCall, result) disconnectDevice(methodCall, result)
} else if (methodCall.method == SCAN_DEVICE_EKG) { } else if (methodCall.method == SCAN_DEVICE_EKG) {
@ -201,6 +200,7 @@ class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivi
} }
@RequiresApi(Build.VERSION_CODES.Q) @RequiresApi(Build.VERSION_CODES.Q)
fun disconnectDevice(methodCall: MethodCall, result: MethodChannel.Result) { fun disconnectDevice(methodCall: MethodCall, result: MethodChannel.Result) {
if (rtHandler.hasCallbacks(rtTask)) rtHandler.removeCallbacks(rtTask) if (rtHandler.hasCallbacks(rtTask)) rtHandler.removeCallbacks(rtTask)
@ -208,7 +208,7 @@ class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivi
} }
private fun scanDevice(methodCall: MethodCall, result: MethodChannel.Result) { private fun scanDevice() {
LiveEventBus.get<Boolean>(EventMsgConst.Ble.EventServiceConnectedAndInterfaceInit).observe(this.mainActivity) { LiveEventBus.get<Boolean>(EventMsgConst.Ble.EventServiceConnectedAndInterfaceInit).observe(this.mainActivity) {
@ -218,274 +218,285 @@ class BleBridge(private var flutterEngine: FlutterEngine, private var mainActivi
println("EventServiceConnectedAndInterfaceInit") println("EventServiceConnectedAndInterfaceInit")
} }
LiveEventBus.get<Boolean>(EventMsgConst.Ble.EventServiceConnectedAndInterfaceInit).observe(this.mainActivity) {
// BleService init success
println("EventServiceConnectedAndInterfaceInit---------")
BleServiceHelper.BleServiceHelper.startScan(models)
println("EventServiceConnectedAndInterfaceInit")
}
LiveEventBus.get<Bluetooth>(EventMsgConst.Discovery.EventDeviceFound).observe(this.mainActivity) { LiveEventBus.get<Bluetooth>(EventMsgConst.Discovery.EventDeviceFound).observe(this.mainActivity) {
var deviceName: String = "" val deviceName: String = ""
for (b in BluetoothController.getDevices()) { val returnData = mapOf("type" to "devicesList", "data" to gson.toJson(BluetoothController.getDevices()))
println(b.name) println("devicesList: ${BluetoothController.getDevices()}");
eventSink?.success(returnData)
//TODO: UNCOMMENT THIS
// if (b.name.contains("POD-1_SN8187", true) || b.name.contains(
// "O2M 1670", true
// ) || b.name.contains("DuoEK", true) || b.name.contains("BP2", true) || b.name.contains("SP-20", true)
// ) {
if (b.name.contains("SP-20", true)) {
model = b.model
deviceName = b.name
BleServiceHelper.BleServiceHelper.setInterfaces(b.model)
BleServiceHelper.BleServiceHelper.stopScan()
BleServiceHelper.BleServiceHelper.connect(
this.mainActivity.applicationContext, b.model, b.device
)
}
}
println("EventDeviceFound") println("EventDeviceFound")
println(deviceName) println(deviceName)
if (deviceName == "POD-1_SN8187") { }
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.PC60Fw.EventPC60FwRtParam).observe(this.mainActivity) { val rawFolders = SparseArray<String>()
val data = it.data as RtParam try {
println("OXY DATA POD-1W: $data") BleServiceHelper.BleServiceHelper.initRawFolder(rawFolders).initService(this.mainActivity.application)
eventSink?.success(data.toString())
} } catch (e: Exception) {
} println(e)
}
}
if (deviceName.contains("DuoEK", ignoreCase = true)) {
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2Info).observe(this.mainActivity) {
val data = it.data as DeviceInfo
println("DuoEK INFO DATA: $data")
val returnData = mapOf("type" to "infoData", "data" to data.toString())
eventSink?.success(returnData) @RequiresApi(Build.VERSION_CODES.Q)
} private fun subscribeToStreams(deviceName: String) {
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2FileList).observe(this.mainActivity) {
BleServiceHelper.BleServiceHelper.stopRtTask(model)
ecgFileNames = it.data as ArrayList<String> // ECG TRACKER
readFile() if (deviceName.contains("DuoEK", ignoreCase = true)) {
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2Info).observe(this.mainActivity) {
val data = it.data as DeviceInfo
println("DuoEK INFO DATA: $data")
val returnData = mapOf("type" to "infoData", "data" to data.toString())
eventSink?.success(returnData)
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2FileList).observe(this.mainActivity) {
BleServiceHelper.BleServiceHelper.stopRtTask(model)
ecgFileNames = it.data as ArrayList<String>
readFile()
// val fileNames = it.data as ArrayList<String> // val fileNames = it.data as ArrayList<String>
// println("DuoEK FileNames List: ${fileNames}") // println("DuoEK FileNames List: ${fileNames}")
// val returnData = // val returnData =
// mapOf("type" to "fileList", "data" to fileNames) // mapOf("type" to "fileList", "data" to fileNames)
// eventSink?.success(returnData) // eventSink?.success(returnData)
} }
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2SetTime).observe(this.mainActivity) { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2SetTime).observe(this.mainActivity) {
println("EventEr2SetTime") println("EventEr2SetTime")
// Get Device Info // Get Device Info
BleServiceHelper.BleServiceHelper.er2GetInfo(model) BleServiceHelper.BleServiceHelper.er2GetInfo(model)
// ecgRTTask.run() // ecgRTTask.run()
} }
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2ReadFileComplete).observe(this.mainActivity) { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2ReadFileComplete).observe(this.mainActivity) {
println("EventEr2ReadFileComplete") println("EventEr2ReadFileComplete")
val data = it.data as Er2File val data = it.data as Er2File
if (data.fileName.contains("R")) { if (data.fileName.contains("R")) {
val file = Er2EcgFile(data.content) val file = Er2EcgFile(data.content)
val ecgShorts = Er1Decompress.unCompressAlgECG(file.waveData) val ecgShorts = Er1Decompress.unCompressAlgECG(file.waveData)
val ecgData = EcgData() val ecgData = EcgData()
val startTime = DateUtil.getSecondTimestamp(data.fileName.replace("R", "")) val startTime = DateUtil.getSecondTimestamp(data.fileName.replace("R", ""))
ecgData.fileName = data.fileName ecgData.fileName = data.fileName
ecgData.duration = file.recordingTime ecgData.duration = file.recordingTime
ecgData.shortData = ecgShorts ecgData.shortData = ecgShorts
ecgData.startTime = startTime ecgData.startTime = startTime
ecgList.add(ecgData) ecgList.add(ecgData)
print(ecgList) print(ecgList)
// val returnData = // val returnData =
// mapOf("type" to "fileDetail", "data" to gson.toJson(ecgList)) // mapOf("type" to "fileDetail", "data" to gson.toJson(ecgList))
// eventSink?.success(returnData) // eventSink?.success(returnData)
}
ecgFileNames.removeAt(0)
readFile()
} }
ecgFileNames.removeAt(0)
readFile()
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2RtData).observe(this.mainActivity) { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.ER2.EventEr2RtData).observe(this.mainActivity) {
val data = it.data as RtData val data = it.data as RtData
println("EventEr2RtData") println("EventEr2RtData")
val returnData = mapOf("type" to "realtimeDataECG", "data" to gson.toJson(data)) val returnData = mapOf("type" to "realtimeDataECG", "data" to gson.toJson(data))
println(returnData) println(returnData)
eventSink?.success(returnData) eventSink?.success(returnData)
// DataController.receive(data.wave.ecgFloats) // DataController.receive(data.wave.ecgFloats)
// hr.text = "${data.param.hr}" // hr.text = "${data.param.hr}"
// data_log.text = "${data.param}" // data_log.text = "${data.param}"
// sampling rate125HZ // sampling rate125HZ
// mV = n * 0.002467data.wave.ecgFloats = data.wave.ecgShorts * 0.002467 // mV = n * 0.002467data.wave.ecgFloats = data.wave.ecgShorts * 0.002467
// data.param.batteryState0no charge1charging2charging complete3low battery // data.param.batteryState0no charge1charging2charging complete3low battery
// data.param.battery0-100 // data.param.battery0-100
// data.param.recordTimeunits // data.param.recordTimeunits
// data.param.curStatus0idle1preparing2measuring3saving file4saving succeed // data.param.curStatus0idle1preparing2measuring3saving file4saving succeed
// 5less than 30s, file not saved66 retests7lead off // 5less than 30s, file not saved66 retests7lead off
} }
}
// OXYMETER
if (deviceName == "POD-1_SN8187" || deviceName == "PC-60FW") { //DONE
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.PC60Fw.EventPC60FwRtParam).observe(this.mainActivity) {
val data = it.data as RtParam
println("POD-1_SN8187: $data")
println("PC-60FW: $data")
val oxyModel = OxymeterModel(data.spo2, data.pr, data.pi, data.isProbeOff, data.isPulseSearching, true, 100);
val returnData = mapOf("type" to "OxyRtParam", "data" to gson.toJson(oxyModel))
eventSink?.success(returnData)
} }
}
if (deviceName == "O2M 1670") { if (deviceName == "O2M 1670" || deviceName == "O2Ring 4135") { // DONE
rtHandler.removeCallbacks(rtTask) rtHandler.removeCallbacks(rtTask)
rtHandler.post(rtTask) rtHandler.post(rtTask)
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.Oxy.EventOxyRtParamData).observe(this.mainActivity) {
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.Oxy.EventOxyRtParamData).observe(this.mainActivity) { val data = it.data as com.lepu.blepro.ext.oxy.RtParam
val data = it.data as com.lepu.blepro.ext.oxy.RtParam println("O2M 1670: $data")
println("OXY DATA O2M: $data") println("O2Ring 4135: $data")
eventSink?.success(data.toString()) val oxyModel = OxymeterModel(data.spo2, data.pr, data.pi, false, true, true, data.battery);
// tv_oxy.text = data.spo2.toString() val returnData = mapOf("type" to "OxyRtParam", "data" to gson.toJson(oxyModel))
// tv_pr.text = data.pr.toString() eventSink?.success(returnData)
// tv_pi.text = data.pi.toString()
// data_log.text = "$data"
// data.battery0-100
// data.batteryState0no charge1charging2charging complete
// data.state0lead off1lead onothererror
}
} }
}
if (deviceName.contains("BP2", ignoreCase = true)) { if (deviceName.contains("SP-20", ignoreCase = true)) { //DONE
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2SyncTime).observe(this.mainActivity) { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20SetTime).observe(this.mainActivity) {
println("EventBp2SyncTime") println("EventSp20SetTime")
// Get Device Info BleServiceHelper.BleServiceHelper.bp2GetInfo(model)
BleServiceHelper.BleServiceHelper.bp2GetInfo(model) }
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2Info).observe(this.mainActivity) { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20DeviceInfo).observe(this.mainActivity) {
val data = it.data as com.lepu.blepro.ext.bp2.DeviceInfo val data = it.data as com.lepu.blepro.ext.sp20.DeviceInfo
println("BP2 INFO DATA: $data") println("EventSp20DeviceInfo: $data")
val returnData = mapOf("type" to "infoData", "data" to data.toString(), "deviceName" to deviceName) val returnData = mapOf("type" to "infoData", "data" to data.toString(), "deviceName" to deviceName)
eventSink?.success(returnData) eventSink?.success(returnData)
BleServiceHelper.BleServiceHelper.startRtTask(model) BleServiceHelper.BleServiceHelper.startRtTask(model)
} }
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2FileList).observe(this.mainActivity) { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20RtParam).observe(this.mainActivity) {
bpFileNames = it.data as ArrayList<String> val data = it.data as com.lepu.blepro.ext.sp20.RtParam
} println("OxyRtParam: $data")
val oxyModel = OxymeterModel(data.spo2, data.pr, data.pi, data.isProbeOff, data.isPulseSearching, data.isCheckProbe, data.battery);
val returnData = mapOf("type" to "OxyRtParam", "data" to gson.toJson(oxyModel))
eventSink?.success(returnData)
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20RtWave).observe(this.mainActivity) {
val data = it.data as RtWave
println("OxyRtParam: $data")
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2ReadFileComplete).observe(this.mainActivity) { }
val data = it.data as Bp2File
// data.type: 1(BP), 2(ECG)
println("LiveEventBus with BP2 : $data")
if (data.type == 1) {
val file = BpFile(data.content)
// file.measureTimeunits
println("BpFile : $file")
} else if (data.type == 2) {
val file = EcgFile(data.content)
val ecgData = EcgData()
val startTime = DateUtil.getSecondTimestamp(data.fileName)
ecgData.fileName = data.fileName
ecgData.duration = file.recordingTime
ecgData.shortData = Er1Decompress.unCompressAlgECG(file.waveData)
// ecgData.shortData = FilterUtil.getEcgFileFilterData(it.model, data.content)
ecgData.startTime = startTime
ecgList.add(ecgData)
// sampling rate125HZ
// mV = file.waveShortData * 0.003098
// file.measureTimeunits
// file.recordingTimeunits
// file.connectCable: Whether the cable is connected
// file.diagnosisEcgDiagnosis
// diagnosis.isRegularWhether Regular ECG Rhythm
// diagnosis.isPoorSignalWhether Unable to analyze
// diagnosis.isLeadOffWhether Always lead off
// diagnosis.isFastHrWhether Fast Heart Rate
// diagnosis.isSlowHrWhether Slow Heart Rate
// diagnosis.isIrregularWhether Irregular ECG Rhythm
// diagnosis.isPvcsWhether Possible ventricular premature beats
// diagnosis.isHeartPauseWhether Possible heart pause
// diagnosis.isFibrillationWhether Possible Atrial fibrillation
// diagnosis.isWideQrsWhether Wide QRS duration
// diagnosis.isProlongedQtcWhether QTc is prolonged
// diagnosis.isShortQtcWhether QTc is short
println("EcgFile : $file")
}
bpFileNames.removeAt(0)
readFileForBp2()
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2RtData).observe(this.mainActivity) { }
val data = it.data as com.lepu.blepro.ext.bp2.RtData
// data.status: RtStatus
// data.status.deviceStatus: 0(STATUS_SLEEP), 1(STATUS_MEMERY), 2(STATUS_CHARGE), 3(STATUS_READY),
// 4(STATUS_BP_MEASURING), 5(STATUS_BP_MEASURE_END),
// 6(STATUS_ECG_MEASURING), 7(STATUS_ECG_MEASURE_END), 20(STATUS_VEN)
// data.status.batteryStatus: 0(no charge), 1(charging), 2(charging complete), 3(low battery)
// data.status.percent: 0-100
// data.param: RtParam
// data.param.paramDataType: 0(Bp measuring), 1(Bp end), 2(Ecg measuring), 3(Ecg end)
when (data.param.paramDataType) {
0 -> {
val bpIng = RtBpIng(data.param.paramData)
val returnData = mapOf("type" to "RealTimeDataBP2Measuring", "data" to gson.toJson(bpIng)) //BLOOD PRESSURE
eventSink?.success(returnData) if (deviceName.contains("BP2", ignoreCase = true)) {
} LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2SyncTime).observe(this.mainActivity) {
println("EventBp2SyncTime")
// Get Device Info
BleServiceHelper.BleServiceHelper.bp2GetInfo(model)
}
1 -> { LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2Info).observe(this.mainActivity) {
val bpResult = RtBpResult(data.param.paramData) val data = it.data as com.lepu.blepro.ext.bp2.DeviceInfo
val returnData = mapOf("type" to "RealTimeDataBP2Result", "data" to gson.toJson(bpResult)) println("BP2 INFO DATA: $data")
println("RealTimeDataBP2Result FOR BP : $returnData") val returnData = mapOf("type" to "infoData", "data" to data.toString(), "deviceName" to deviceName)
eventSink?.success(returnData) eventSink?.success(returnData)
BleServiceHelper.BleServiceHelper.startRtTask(model)
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2FileList).observe(this.mainActivity) {
bpFileNames = it.data as ArrayList<String>
}
} LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2ReadFileComplete).observe(this.mainActivity) {
val data = it.data as Bp2File
// data.type: 1(BP), 2(ECG)
println("LiveEventBus with BP2 : $data")
if (data.type == 1) {
val file = BpFile(data.content)
// file.measureTimeunits
println("BpFile : $file")
} else if (data.type == 2) {
val file = EcgFile(data.content)
val ecgData = EcgData()
val startTime = DateUtil.getSecondTimestamp(data.fileName)
ecgData.fileName = data.fileName
ecgData.duration = file.recordingTime
ecgData.shortData = Er1Decompress.unCompressAlgECG(file.waveData)
// ecgData.shortData = FilterUtil.getEcgFileFilterData(it.model, data.content)
ecgData.startTime = startTime
ecgList.add(ecgData)
// sampling rate125HZ
// mV = file.waveShortData * 0.003098
// file.measureTimeunits
// file.recordingTimeunits
// file.connectCable: Whether the cable is connected
// file.diagnosisEcgDiagnosis
// diagnosis.isRegularWhether Regular ECG Rhythm
// diagnosis.isPoorSignalWhether Unable to analyze
// diagnosis.isLeadOffWhether Always lead off
// diagnosis.isFastHrWhether Fast Heart Rate
// diagnosis.isSlowHrWhether Slow Heart Rate
// diagnosis.isIrregularWhether Irregular ECG Rhythm
// diagnosis.isPvcsWhether Possible ventricular premature beats
// diagnosis.isHeartPauseWhether Possible heart pause
// diagnosis.isFibrillationWhether Possible Atrial fibrillation
// diagnosis.isWideQrsWhether Wide QRS duration
// diagnosis.isProlongedQtcWhether QTc is prolonged
// diagnosis.isShortQtcWhether QTc is short
println("EcgFile : $file")
}
bpFileNames.removeAt(0)
readFileForBp2()
}
} LiveEventBus.get<InterfaceEvent>(InterfaceEvent.BP2.EventBp2RtData).observe(this.mainActivity) {
val data = it.data as com.lepu.blepro.ext.bp2.RtData
// data.status: RtStatus
// data.status.deviceStatus: 0(STATUS_SLEEP), 1(STATUS_MEMERY), 2(STATUS_CHARGE), 3(STATUS_READY),
// 4(STATUS_BP_MEASURING), 5(STATUS_BP_MEASURE_END),
// 6(STATUS_ECG_MEASURING), 7(STATUS_ECG_MEASURE_END), 20(STATUS_VEN)
// data.status.batteryStatus: 0(no charge), 1(charging), 2(charging complete), 3(low battery)
// data.status.percent: 0-100
// data.param: RtParam
// data.param.paramDataType: 0(Bp measuring), 1(Bp end), 2(Ecg measuring), 3(Ecg end)
when (data.param.paramDataType) {
0 -> {
val bpIng = RtBpIng(data.param.paramData)
} val returnData = mapOf("type" to "RealTimeDataBP2Measuring", "data" to gson.toJson(bpIng))
eventSink?.success(returnData)
} }
if (deviceName.contains("SP-20", ignoreCase = true)) { 1 -> {
val bpResult = RtBpResult(data.param.paramData)
val returnData = mapOf("type" to "RealTimeDataBP2Result", "data" to gson.toJson(bpResult))
println("RealTimeDataBP2Result FOR BP : $returnData")
eventSink?.success(returnData)
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20SetTime).observe(this.mainActivity) {
println("EventSp20SetTime")
BleServiceHelper.BleServiceHelper.bp2GetInfo(model)
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20DeviceInfo).observe(this.mainActivity) { }
val data = it.data as com.lepu.blepro.ext.sp20.DeviceInfo
println("EventSp20DeviceInfo: $data")
val returnData = mapOf("type" to "infoData", "data" to data.toString(), "deviceName" to deviceName)
eventSink?.success(returnData)
BleServiceHelper.BleServiceHelper.startRtTask(model)
}
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20RtParam).observe(this.mainActivity) {
val data = it.data as com.lepu.blepro.ext.sp20.RtParam
println("EventSp20RtParam: $data")
val returnData = mapOf("type" to "EventSp20RtParam", "data" to gson.toJson(data))
eventSink?.success(returnData)
} }
LiveEventBus.get<InterfaceEvent>(InterfaceEvent.SP20.EventSp20RtWave).observe(this.mainActivity) {
val data = it.data as RtWave
println("EventSp20RtWave: $data")
}
} }
} }
val rawFolders = SparseArray<String>()
try {
BleServiceHelper.BleServiceHelper.initRawFolder(rawFolders).initService(this.mainActivity.application)
} catch (e: Exception) {
println(e) }
private fun connectDevice(device: List<String>) {
println("connectDevice: $device");
model = device[1].toInt()
val deviceName = device[0]
for (b in BluetoothController.getDevices()) {
println("getDevices device: ${b.name}")
if (b.name == deviceName) {
BleServiceHelper.BleServiceHelper.setInterfaces(b.model)
// BleServiceHelper.BleServiceHelper.stopScan()
BleServiceHelper.BleServiceHelper.connect(
this.mainActivity.applicationContext, model, b.device
)
subscribeToStreams(deviceName);
}
} }
} }
private fun readFile() { private fun readFile() {
if (ecgFileNames.size == 0) { if (ecgFileNames.size == 0) {
val returnData = mapOf("type" to "fileDetail", "data" to gson.toJson(ecgList)) val returnData = mapOf("type" to "fileDetail", "data" to gson.toJson(ecgList))

@ -0,0 +1,22 @@
package com.cloud.diplomaticquarterapp.ble;
public class OxymeterModel {
public int spo2;
public int pr;
public float pi;
public boolean isProbeOff;
public boolean isPulseSearching;
public boolean isCheckProbe;
public int battery;
public OxymeterModel(int spo2, int pr, float pi, boolean isProbeOff, boolean isPulseSearching, boolean isCheckProbe, int battery) {
this.spo2 = spo2;
this.pr = pr;
this.pi = pi;
this.isProbeOff = isProbeOff;
this.isPulseSearching = isPulseSearching;
this.isCheckProbe = isCheckProbe;
this.battery = battery;
}
}

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:diplomaticquarterapp/core/viewModels/PharmacyPagesViewModel.dart'; import 'package:diplomaticquarterapp/core/viewModels/PharmacyPagesViewModel.dart';
import 'package:diplomaticquarterapp/core/viewModels/dashboard_view_model.dart'; import 'package:diplomaticquarterapp/core/viewModels/dashboard_view_model.dart';
import 'package:diplomaticquarterapp/models/Appointments/toDoCountProviderModel.dart'; import 'package:diplomaticquarterapp/models/Appointments/toDoCountProviderModel.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart';
import 'package:diplomaticquarterapp/routes.dart'; import 'package:diplomaticquarterapp/routes.dart';
import 'package:diplomaticquarterapp/services/robo_search/event_provider.dart'; import 'package:diplomaticquarterapp/services/robo_search/event_provider.dart';
import 'package:diplomaticquarterapp/services/robo_search/search_provider.dart'; import 'package:diplomaticquarterapp/services/robo_search/search_provider.dart';
@ -118,12 +119,15 @@ class _MyApp extends State<MyApp> {
), ),
ChangeNotifierProvider<CompareList>(create: (context) => CompareList()), ChangeNotifierProvider<CompareList>(create: (context) => CompareList()),
ChangeNotifierProvider<OrderPreviewViewModel>(create: (context) => OrderPreviewViewModel()), ChangeNotifierProvider<OrderPreviewViewModel>(create: (context) => OrderPreviewViewModel()),
ChangeNotifierProvider<MyTrackersViewModel>(create: (context) => MyTrackersViewModel()),
], ],
child: Consumer<ProjectViewModel>( child: Consumer<ProjectViewModel>(
builder: (context, projectProvider, child) => MaterialApp( builder: (context, projectProvider, child) => MaterialApp(
builder: (BuildContext context, Widget child) { builder: (BuildContext context, Widget child) {
return MediaQuery( return MediaQuery(
data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0,), //set desired text scale factor here data: MediaQuery.of(context).copyWith(
textScaleFactor: 1.0,
), //set desired text scale factor here
child: child, child: child,
); );
}, },

@ -1,24 +0,0 @@
class BpRtDataMeasuringModel {
bool deflate;
int pr;
int pressure;
bool pulse;
BpRtDataMeasuringModel({this.deflate, this.pr, this.pressure, this.pulse});
BpRtDataMeasuringModel.fromJson(Map<String, dynamic> json) {
deflate = json['deflate'];
pr = json['pr'];
pressure = json['pressure'];
pulse = json['pulse'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['deflate'] = this.deflate;
data['pr'] = this.pr;
data['pressure'] = this.pressure;
data['pulse'] = this.pulse;
return data;
}
}

@ -1,33 +0,0 @@
class BpRtDataResultModel {
bool deflate;
int dia;
int mean;
int pr;
int pressure;
int result;
int sys;
BpRtDataResultModel({this.deflate, this.dia, this.mean, this.pr, this.pressure, this.result, this.sys});
BpRtDataResultModel.fromJson(Map<String, dynamic> json) {
deflate = json['deflate'];
dia = json['dia'];
mean = json['mean'];
pr = json['pr'];
pressure = json['pressure'];
result = json['result'];
sys = json['sys'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['deflate'] = this.deflate;
data['dia'] = this.dia;
data['mean'] = this.mean;
data['pr'] = this.pr;
data['pressure'] = this.pressure;
data['result'] = this.result;
data['sys'] = this.sys;
return data;
}
}

@ -1,4 +1,4 @@
import 'package:diplomaticquarterapp/viatom_ble/ble_connect.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';

@ -15,7 +15,9 @@ import 'package:provider/provider.dart';
class MedicalProfilePageNew extends StatefulWidget { class MedicalProfilePageNew extends StatefulWidget {
Function onTap; Function onTap;
String a; String a;
MedicalProfilePageNew({this.onTap}); MedicalProfilePageNew({this.onTap});
@override @override
_MedicalProfilePageState createState() => _MedicalProfilePageState(); _MedicalProfilePageState createState() => _MedicalProfilePageState();
} }

@ -0,0 +1,93 @@
import 'dart:developer';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/device_types/oxymeter_connect_screen.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart';
import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BleDevicesScreen extends StatefulWidget {
@override
State<BleDevicesScreen> createState() => _BleDevicesScreenState();
}
class _BleDevicesScreenState extends State<BleDevicesScreen> {
MyTrackersViewModel myTrackersVm;
@override
void dispose() {
myTrackersVm.resetList();
super.dispose();
}
@override
void initState() {
myTrackersVm = context.read<MyTrackersViewModel>();
myTrackersVm.startSearchingForTracker();
super.initState();
}
@override
Widget build(BuildContext context) {
return AppScaffold(
appBarTitle: "BLE Tracker",
showNewAppBar: true,
isShowDecPage: false,
showNewAppBarTitle: true,
backgroundColor: Color(0xffF8F8F8),
body: Container(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Consumer(
builder: (BuildContext context, MyTrackersViewModel myTrackerVm, Widget child) {
if (myTrackerVm.devicesList.isEmpty) {
return Padding(
padding: const EdgeInsets.all(24.0),
child: Center(
child: Text(
"Searching for Devices Animation",
style: TextStyle(fontSize: 9.0),
),
),
);
}
return Padding(
padding: EdgeInsets.only(left: 12, right: 12, top: 20),
child: GridView.builder(
shrinkWrap: true,
primary: false,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12),
padding: EdgeInsets.zero,
itemCount: myTrackerVm.devicesList.length,
itemBuilder: (BuildContext context, int index) {
BleDeviceModel device = myTrackerVm.devicesList[index];
return InkWell(
onTap: () {
myTrackerVm.oxyRtModel = null;
Navigator.pushReplacement(context, FadePage(page: OxymeterConnectScreen(deviceModel: device)));
},
child: MedicalProfileItem(
title: "${device.name}",
imagePath: 'tracker.svg',
subTitle: "",
isEnable: true,
),
);
},
),
);
},
),
],
),
),
),
);
}
}

@ -0,0 +1,140 @@
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart';
import 'package:diplomaticquarterapp/uitl/utils_new.dart';
import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BloodPressureConnectScreen extends StatefulWidget {
final BleDeviceModel deviceModel;
const BloodPressureConnectScreen({this.deviceModel});
@override
State<BloodPressureConnectScreen> createState() => _BloodPressureConnectScreenState();
}
class _BloodPressureConnectScreenState extends State<BloodPressureConnectScreen> {
MyTrackersViewModel myTrackersVm;
@override
void initState() {
myTrackersVm = context.read<MyTrackersViewModel>();
myTrackersVm.connectDevice(widget.deviceModel);
super.initState();
}
@override
void dispose() {
myTrackersVm.disConnectDevice();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AppScaffold(
appBarTitle: "${widget.deviceModel.name}",
showNewAppBar: true,
isShowDecPage: false,
showNewAppBarTitle: true,
backgroundColor: Color(0xffF8F8F8),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: Consumer(
builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: ListView(
children: [
if (myTrackersViewModel.bpCurrentStatus == "RealTimeDataBP2Measuring") ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
mHeight(24.0),
Column(
children: [
Text(myTrackersViewModel.bpRtMeasuringModel.pressure.toString(), style: TextStyle(fontSize: 100, fontWeight: FontWeight.bold)),
Text("Pressure", style: TextStyle(fontSize: 20)),
],
),
mHeight(24.0),
],
),
] else if (myTrackersViewModel.bpCurrentStatus == "RealTimeDataBP2Result") ...[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
mHeight(24.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(
children: [
Text("Dia", style: TextStyle(fontSize: 20)),
Text(myTrackersViewModel.bpRtResultModel.dia.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
Text("mmHg", style: TextStyle(fontSize: 10)),
],
),
Column(
children: [
Text("Sys", style: TextStyle(fontSize: 20)),
Text(myTrackersViewModel.bpRtResultModel.sys.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
Text("mmHg", style: TextStyle(fontSize: 10)),
],
),
Column(
children: [
Text("♥︎", style: TextStyle(fontSize: 20)),
Text(myTrackersViewModel.bpRtResultModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
Text("/min", style: TextStyle(fontSize: 10)),
],
),
],
),
mHeight(24.0),
],
),
] else ...[
Padding(
padding: const EdgeInsets.all(24.0),
child: Center(
child: Text(
"Some animation with the instruction",
style: TextStyle(fontSize: 9.0),
),
),
)
],
],
),
),
if (myTrackersViewModel.bpCurrentStatus != null) ...[
Row(
children: [
Expanded(
child: DefaultButton(
"Disconnect with ${widget.deviceModel.name}",
() async {
myTrackersVm.bpCurrentStatus = null;
myTrackersVm.bpRtMeasuringModel = null;
myTrackersVm.bpRtResultModel = null;
Navigator.pop(context);
},
textColor: Colors.white,
),
),
],
),
]
],
);
},
),
),
);
}
}

@ -0,0 +1,129 @@
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart';
import 'package:diplomaticquarterapp/uitl/utils_new.dart';
import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class OxymeterConnectScreen extends StatefulWidget {
final BleDeviceModel deviceModel;
const OxymeterConnectScreen({this.deviceModel});
@override
State<OxymeterConnectScreen> createState() => _OxymeterConnectScreenState();
}
class _OxymeterConnectScreenState extends State<OxymeterConnectScreen> {
MyTrackersViewModel myTrackersVm;
@override
void initState() {
myTrackersVm = context.read<MyTrackersViewModel>();
myTrackersVm.connectDevice(widget.deviceModel);
super.initState();
}
@override
void dispose() {
myTrackersVm.disConnectDevice();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AppScaffold(
appBarTitle: "${widget.deviceModel.name}",
showNewAppBar: true,
isShowDecPage: false,
showNewAppBarTitle: true,
backgroundColor: Color(0xffF8F8F8),
body: Padding(
padding: const EdgeInsets.all(24.0),
child: Consumer(
builder: (BuildContext context, MyTrackersViewModel myTrackersViewModel, Widget child) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: ListView(
children: [
if (myTrackersViewModel.oxyRtModel != null) ...[
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
mHeight(24.0),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
if (myTrackersViewModel.oxyRtModel.isProbeOff) ...[
Text("Check Probe", style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
] else ...[
Column(
children: [
Text("SPO₂", style: TextStyle(fontSize: 20)),
Text(myTrackersViewModel.oxyRtModel.spo2.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
Text("%", style: TextStyle(fontSize: 10)),
],
),
Column(
children: [
Text("Pulse", style: TextStyle(fontSize: 20)),
Text(myTrackersViewModel.oxyRtModel.pr.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
Text("bpm", style: TextStyle(fontSize: 10)),
],
),
Column(
children: [
Text("PI", style: TextStyle(fontSize: 20)),
Text(myTrackersViewModel.oxyRtModel.pi.toString(), style: TextStyle(fontSize: 50, fontWeight: FontWeight.bold)),
Text("%", style: TextStyle(fontSize: 10)),
],
),
],
],
),
mHeight(24.0),
],
)
],
if (myTrackersViewModel.oxyRtModel == null) ...[
Padding(
padding: const EdgeInsets.all(24.0),
child: Center(
child: Text(
"Some animation with the instruction",
style: TextStyle(fontSize: 9.0),
),
),
),
]
],
),
),
if (myTrackersViewModel.oxyRtModel != null) ...[
Row(
children: [
Expanded(
child: DefaultButton(
"Disconnect with ${widget.deviceModel.name}",
() async {
myTrackersVm.oxyRtModel = null;
Navigator.pop(context);
},
textColor: Colors.white,
),
),
],
),
]
],
);
},
),
),
);
}
}

@ -0,0 +1,114 @@
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/ble_devices_screen.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers_view_model/my_trackers_view_model.dart';
import 'package:diplomaticquarterapp/widgets/data_display/medical/medical_profile_item.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class SelectTrackerType extends StatelessWidget {
const SelectTrackerType();
List<Widget> myTrackersTypeList({BuildContext context}) {
List<Widget> medical = [];
medical.add(InkWell(
onTap: () {
context.read<MyTrackersViewModel>().updateSelectedTrackerType(TrackerTypeEnum.OxymeterTracker);
Navigator.of(context).push(FadePage(page: BleDevicesScreen()));
},
child: MedicalProfileItem(
title: "Oxymeter",
imagePath: 'tracker.svg',
subTitle: "Tracker",
isEnable: true,
),
));
medical.add(InkWell(
onTap: () {
context.read<MyTrackersViewModel>().updateSelectedTrackerType(TrackerTypeEnum.BloodPressureTracker);
Navigator.of(context).push(FadePage(page: BleDevicesScreen()));
},
child: MedicalProfileItem(
title: "Blood Pressure",
imagePath: 'tracker.svg',
subTitle: "Tracker",
isEnable: true,
),
));
medical.add(InkWell(
onTap: () {
context.read<MyTrackersViewModel>().updateSelectedTrackerType(TrackerTypeEnum.BloodSugarTracker);
Navigator.of(context).push(FadePage(page: BleDevicesScreen()));
},
child: MedicalProfileItem(
title: "Blood Sugar",
imagePath: 'tracker.svg',
subTitle: "Tracker",
isEnable: true,
),
));
medical.add(InkWell(
onTap: () {
context.read<MyTrackersViewModel>().updateSelectedTrackerType(TrackerTypeEnum.ECGTracker);
Navigator.of(context).push(FadePage(page: BleDevicesScreen()));
},
child: MedicalProfileItem(
title: "ECG",
imagePath: 'tracker.svg',
subTitle: "Tracker",
isEnable: true,
),
));
medical.add(InkWell(
onTap: () {
context.read<MyTrackersViewModel>().updateSelectedTrackerType(TrackerTypeEnum.AllInOneTracker);
Navigator.of(context).push(FadePage(page: BleDevicesScreen()));
},
child: MedicalProfileItem(
title: "All in one Monitor",
imagePath: 'tracker.svg',
subTitle: "Tracker",
isEnable: true,
),
));
return medical;
}
@override
Widget build(BuildContext context) {
List<Widget> myMedicalList = myTrackersTypeList(context: context);
return AppScaffold(
isShowDecPage: false,
isShowAppBar: false,
showNewAppBar: true,
isHelp: true,
showNewAppBarTitle: true,
appBarTitle: "Select Tracker",
body: Container(
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(left: 12, right: 12, top: 20),
child: GridView.builder(
shrinkWrap: true,
primary: false,
physics: NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3, childAspectRatio: 2 / 2, crossAxisSpacing: 12, mainAxisSpacing: 12),
padding: EdgeInsets.zero,
itemCount: myMedicalList.length,
itemBuilder: (BuildContext context, int index) {
return myMedicalList[index];
},
),
),
],
),
),
),
);
}
}

@ -10,14 +10,14 @@ class BleChannel {
// static const platform_ios_ekg = MethodChannel('BLE-Platform-Bridge-IOS-EKG'); // static const platform_ios_ekg = MethodChannel('BLE-Platform-Bridge-IOS-EKG');
//BLE-Platform-Bridge //BLE-Platform-Bridge
static Future<String> getScanningResult(List<String> deviceType) async { static Future<String> scanResults(List<String> deviceType) async {
try { try {
String result; String result;
print("----------Flutter Init -------"); print("----------Flutter Init -------");
// if (Platform.isIOS && deviceType[0] == "ekg") { // if (Platform.isIOS && deviceType[0] == "ekg") {
// result = await platform_ios_ekg.invokeMethod('scanEKG', deviceType); // result = await platform_ios_ekg.invokeMethod('scanEKG', deviceType);
// } else { // } else {
result = await platform.invokeMethod('scan', deviceType); result = await platform.invokeMethod('scan');
// } // }
print("----------Flutter Result -------"); print("----------Flutter Result -------");
print(result); print(result);
@ -27,6 +27,23 @@ class BleChannel {
} }
} }
static Future<String> connectDevice(List<String> device) async {
try {
String result;
print("----------Flutter init connectDevice -------");
// if (Platform.isIOS && deviceType[0] == "ekg") {
// result = await platform_ios_ekg.invokeMethod('scanEKG', deviceType);
// } else {
result = await platform.invokeMethod('connectDevice', device);
// }
print("----------Flutter Result connectDevice -------");
print(result);
return result;
} catch (e) {
return "Error: $e";
}
}
static Future<String> getEKGFilesList(List<String> deviceType) async { static Future<String> getEKGFilesList(List<String> deviceType) async {
try { try {
print("----------Flutter Init -------"); print("----------Flutter Init -------");
@ -65,7 +82,7 @@ class BleChannel {
static Future<String> disconnect() async { static Future<String> disconnect() async {
try { try {
print("----------Flutter Init -------"); print("----------Flutter Init disconnect_device -------");
final String result = await platform.invokeMethod('disconnect_device'); final String result = await platform.invokeMethod('disconnect_device');
print("----------Flutter Result -------"); print("----------Flutter Result -------");
print(result); print(result);

@ -0,0 +1,29 @@
class BleDeviceModel {
String macAddr;
int model;
String name;
int rssi;
BleDeviceModel({this.macAddr, this.model, this.name, this.rssi});
BleDeviceModel.fromJson(Map<String, dynamic> json) {
macAddr = json['macAddr'];
model = json['model'];
name = json['name'];
rssi = json['rssi'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['macAddr'] = this.macAddr;
data['model'] = this.model;
data['name'] = this.name;
data['rssi'] = this.rssi;
return data;
}
@override
String toString() {
return 'BleDeviceModel{macAddr: $macAddr, model: $model, name: $name, rssi: $rssi}';
}
}

@ -0,0 +1,58 @@
class BpRtMeasuringModel {
bool deflate;
int pr;
int pressure;
bool pulse;
BpRtMeasuringModel({this.deflate, this.pr, this.pressure, this.pulse});
BpRtMeasuringModel.fromJson(Map<String, dynamic> json) {
deflate = json['deflate'];
pr = json['pr'];
pressure = json['pressure'];
pulse = json['pulse'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['deflate'] = this.deflate;
data['pr'] = this.pr;
data['pressure'] = this.pressure;
data['pulse'] = this.pulse;
return data;
}
}
class BpRtResultModel {
bool deflate;
int dia;
int mean;
int pr;
int pressure;
int result;
int sys;
BpRtResultModel({this.deflate, this.dia, this.mean, this.pr, this.pressure, this.result, this.sys});
BpRtResultModel.fromJson(Map<String, dynamic> json) {
deflate = json['deflate'];
dia = json['dia'];
mean = json['mean'];
pr = json['pr'];
pressure = json['pressure'];
result = json['result'];
sys = json['sys'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['deflate'] = this.deflate;
data['dia'] = this.dia;
data['mean'] = this.mean;
data['pr'] = this.pr;
data['pressure'] = this.pressure;
data['result'] = this.result;
data['sys'] = this.sys;
return data;
}
}

@ -1,4 +1,4 @@
class SP20RTParamModel { class OxyRtParamModel {
int battery; int battery;
bool isCheckProbe; bool isCheckProbe;
bool isProbeOff; bool isProbeOff;
@ -7,9 +7,9 @@ class SP20RTParamModel {
int pr; int pr;
int spo2; int spo2;
SP20RTParamModel({this.battery, this.isCheckProbe, this.isProbeOff, this.isPulseSearching, this.pi, this.pr, this.spo2}); OxyRtParamModel({this.battery, this.isCheckProbe, this.isProbeOff, this.isPulseSearching, this.pi, this.pr, this.spo2});
SP20RTParamModel.fromJson(Map<String, dynamic> json) { OxyRtParamModel.fromJson(Map<String, dynamic> json) {
battery = json['battery']; battery = json['battery'];
isCheckProbe = json['isCheckProbe']; isCheckProbe = json['isCheckProbe'];
isProbeOff = json['isProbeOff']; isProbeOff = json['isProbeOff'];

@ -0,0 +1,171 @@
import 'dart:convert';
import 'dart:developer';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/ble_devices_model.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_param_model.dart';
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart';
import 'package:flutter/material.dart';
enum TrackerTypeEnum { OxymeterTracker, BloodPressureTracker, BloodSugarTracker, ECGTracker, AllInOneTracker }
class MyTrackersViewModel extends ChangeNotifier {
EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event');
List<BleDeviceModel> devicesList = [];
var devicesInfoJson = {
"OxymeterTracker": [
{"model": "SP-20"}, // DONE
{"model": "POD-1"}, // DONE
{"model": "O2M"}, // DONE
{"model": "PC-60"}, //DONE
{"model": "O2Ring"}, // DONE
],
"BloodPressureTracker": [
{"model": "BP2"},
{"model": ""},
{"model": ""},
{"model": ""},
],
"BloodSugarTracker": [
{"model": ""},
{"model": ""},
{"model": ""},
{"model": ""},
],
"ECGTracker": [
{"model": "DuoEK"},
{"model": ""},
{"model": ""},
{"model": ""},
],
"AllInOneTracker": [
{"model": ""},
{"model": ""},
{"model": ""},
{"model": ""},
],
};
//************************************* OXYMETER *************************************
OxyRtParamModel oxyRtModel;
void updateOxyRtModel(Map<String, dynamic> mapData) {
oxyRtModel = OxyRtParamModel.fromJson(mapData);
notifyListeners();
}
// ************************************* BLOOD PRESSURE *************************************
BpRtMeasuringModel bpRtMeasuringModel;
void updateBpRtMeasuringModel(Map<String, dynamic> mapData) {
bpRtMeasuringModel = BpRtMeasuringModel.fromJson(mapData);
notifyListeners();
}
BpRtResultModel bpRtResultModel;
void updateBpRtResultModel(Map<String, dynamic> mapData) {
bpRtResultModel = BpRtResultModel.fromJson(mapData);
notifyListeners();
}
String bpCurrentStatus;
updateBpCurrentStatus(var value) {
bpCurrentStatus = value;
notifyListeners();
}
void filterOutTheSearchedDevices(List<BleDeviceModel> allDevices) {
devicesList.clear();
notifyListeners();
if (devicesInfoJson.containsKey(currentSelectedTrackerType.name)) {
allDevices.forEach((foundDevice) {
List devicesInSelectedType = devicesInfoJson[currentSelectedTrackerType.name];
for (var device in devicesInSelectedType) {
log("foundDevice.name: ${foundDevice.name.toString()}");
log("device['model']: ${device['model']}");
if (device['model'] != "" && foundDevice.name.contains(device['model'])) {
devicesList.add(foundDevice);
}
}
});
notifyListeners();
}
}
TrackerTypeEnum currentSelectedTrackerType = TrackerTypeEnum.OxymeterTracker;
updateSelectedTrackerType(var value) {
devicesList.clear();
currentSelectedTrackerType = value;
notifyListeners();
}
Future checkBLEPermissions() async {
return [Permission.location, Permission.storage, Permission.bluetooth, Permission.bluetoothConnect, Permission.bluetoothScan].request();
}
Future<void> startSearchingForTracker() async {
log("selectedStracker: ${currentSelectedTrackerType.name}");
await checkBLEPermissions();
eventChannel.receiveBroadcastStream().listen((event) {
print('Received event---: $event');
print(event['type']);
if (event['type'] == "devicesList") {
parsesDevicesList(json.decode(event['data']) as List);
}
if (event['type'] == "OxyRtParam") {
updateOxyRtModel(json.decode(event['data']));
}
if (event['type'] == "fileList") {
print(event['data']);
}
if (event['type'] == 'fileDetail') {
print(event['data']);
}
if (event['type'] == "RealTimeDataBP2Measuring") {
updateBpRtMeasuringModel(json.decode(event['data']));
updateBpCurrentStatus("RealTimeDataBP2Measuring");
}
if (event['type'] == "RealTimeDataBP2Result") {
updateBpRtResultModel(json.decode(event['data']));
updateBpCurrentStatus("RealTimeDataBP2Result");
}
});
await scanDevices();
}
void parsesDevicesList(List returnData) {
var devicesList = List.generate(returnData.length, (index) => BleDeviceModel.fromJson(returnData[index]));
filterOutTheSearchedDevices(devicesList);
}
void resetList() {
devicesList.clear();
// BleChannel.disconnect();
}
Future<void> connectDevice(BleDeviceModel device) async {
await BleChannel.connectDevice([device.name, device.model.toString()]);
}
Future<void> disConnectDevice() async {
await BleChannel.disconnect();
}
Future<void> scanDevices() async {
await BleChannel.scanResults([]);
}
}

@ -1,10 +1,9 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/bp_rt_data_measuring_model.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/bp_rt_model.dart';
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/bp_rt_data_result_model.dart';
import 'package:diplomaticquarterapp/uitl/utils_new.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart';
import 'package:diplomaticquarterapp/viatom_ble/ble_connect.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart';
import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -19,8 +18,8 @@ class BpTrackerBLE extends StatefulWidget {
class _BpTrackerBLEState extends State<BpTrackerBLE> { class _BpTrackerBLEState extends State<BpTrackerBLE> {
EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event');
final bpDataNotifierMeasuring = ValueNotifier<BpRtDataMeasuringModel>(BpRtDataMeasuringModel()); final bpDataNotifierMeasuring = ValueNotifier<BpRtMeasuringModel>(BpRtMeasuringModel());
final bpDataNotifierResult = ValueNotifier<BpRtDataResultModel>(BpRtDataResultModel()); final bpDataNotifierResult = ValueNotifier<BpRtResultModel>(BpRtResultModel());
final bpStatusNotifier = ValueNotifier<String>("status"); final bpStatusNotifier = ValueNotifier<String>("status");
final bpDeviceDataNotifier = ValueNotifier<String>(""); final bpDeviceDataNotifier = ValueNotifier<String>("");
String deviceName = "BP2"; String deviceName = "BP2";
@ -31,7 +30,7 @@ class _BpTrackerBLEState extends State<BpTrackerBLE> {
bpDataNotifierResult.dispose(); bpDataNotifierResult.dispose();
bpStatusNotifier.dispose(); bpStatusNotifier.dispose();
super.dispose(); super.dispose();
BleChannel.disconnect(); // BleChannel.disconnect();
} }
@override @override
@ -81,7 +80,7 @@ class _BpTrackerBLEState extends State<BpTrackerBLE> {
if (value == "RealTimeDataBP2Measuring") { if (value == "RealTimeDataBP2Measuring") {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: bpDataNotifierMeasuring, valueListenable: bpDataNotifierMeasuring,
builder: (context, BpRtDataMeasuringModel bpRtDataMeasuringModel, _) { builder: (context, BpRtMeasuringModel bpRtDataMeasuringModel, _) {
return Row( return Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -100,7 +99,7 @@ class _BpTrackerBLEState extends State<BpTrackerBLE> {
} else if (value == "RealTimeDataBP2Result") { } else if (value == "RealTimeDataBP2Result") {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: bpDataNotifierResult, valueListenable: bpDataNotifierResult,
builder: (context, BpRtDataResultModel bpRtDataResultModel, _) { builder: (context, BpRtResultModel bpRtDataResultModel, _) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -255,15 +254,15 @@ class _BpTrackerBLEState extends State<BpTrackerBLE> {
bpStatusNotifier.value = "RealTimeDataBP2Result"; bpStatusNotifier.value = "RealTimeDataBP2Result";
} }
}); });
await BleChannel.getScanningResult(["bloodpressure", "BP2"]); await BleChannel.scanResults(["bloodpressure", "BP2"]);
}); });
} }
void parseBpRtMeasuringObject(dynamic returnData) { void parseBpRtMeasuringObject(dynamic returnData) {
bpDataNotifierMeasuring.value = BpRtDataMeasuringModel.fromJson(json.decode(returnData)); bpDataNotifierMeasuring.value = BpRtMeasuringModel.fromJson(json.decode(returnData));
} }
void parseBpRtResultObject(dynamic returnData) { void parseBpRtResultObject(dynamic returnData) {
bpDataNotifierResult.value = BpRtDataResultModel.fromJson(json.decode(returnData)); bpDataNotifierResult.value = BpRtResultModel.fromJson(json.decode(returnData));
} }
} }

@ -1,4 +1,4 @@
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ekg_file_detail_response_model.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

@ -1,12 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ekg_file_detail_response_model.dart';
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_file_detail_response_model.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/ekg_realtime_data_response.dart';
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/ekg_realtime_data_response.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/viatom_devices/ekg_chart_view.dart';
import 'package:diplomaticquarterapp/uitl/date_uitl.dart'; import 'package:diplomaticquarterapp/uitl/date_uitl.dart';
import 'package:diplomaticquarterapp/uitl/utils_new.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart';
import 'package:diplomaticquarterapp/viatom_ble/ble_connect.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart';
import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart'; import 'package:diplomaticquarterapp/widgets/transitions/fade_page.dart';
@ -170,7 +169,7 @@ class _EKG_BLEState extends State<EKG_BLE> {
// eventChannelIOSEKG.receiveBroadcastStream().listen((event) { // eventChannelIOSEKG.receiveBroadcastStream().listen((event) {
// print('Received eventChannelIOSEKG event---: $event'); // print('Received eventChannelIOSEKG event---: $event');
// }); // });
await BleChannel.getScanningResult(["ekg", "DuoEK"]); await BleChannel.scanResults(["ekg", "DuoEK"]);
}, },
textColor: Colors.white, textColor: Colors.white,
), ),

@ -1,6 +1,6 @@
import 'dart:io'; import 'dart:io';
import 'package:diplomaticquarterapp/viatom_ble/ble_connect.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -110,7 +110,7 @@ class _Oxymeter_BLEState extends State<Oxymeter_BLE> {
// POD-1W response: RtParam{spo2=98, pr=104, pi=5.8, isProbeOff=false, isPulseSearching=false} // POD-1W response: RtParam{spo2=98, pr=104, pi=5.8, isProbeOff=false, isPulseSearching=false}
}); });
await BleChannel.getScanningResult(["oximeter", deviceName]); await BleChannel.scanResults(["oximeter", deviceName]);
}); });
} }

@ -1,7 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'package:diplomaticquarterapp/models/ble_devices/viatom_devices/oxy_sp20_rt_param_model.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/viatom_devices/oxy_rt_param_model.dart';
import 'package:diplomaticquarterapp/uitl/utils_new.dart'; import 'package:diplomaticquarterapp/uitl/utils_new.dart';
import 'package:diplomaticquarterapp/viatom_ble/ble_connect.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_helpers/ble_connect_helper.dart';
import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart'; import 'package:diplomaticquarterapp/widgets/buttons/defaultButton.dart';
import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart'; import 'package:diplomaticquarterapp/widgets/others/app_scaffold_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -17,7 +17,7 @@ class _SP20PulseTrackerState extends State<SP20PulseTracker> {
EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event'); EventChannel eventChannel = EventChannel('BLE-Platform-Bridge-Event');
// final bpDataNotifierMeasuring = ValueNotifier<BpRtDataMeasuringModel>(BpRtDataMeasuringModel()); // final bpDataNotifierMeasuring = ValueNotifier<BpRtDataMeasuringModel>(BpRtDataMeasuringModel());
final sp20RtParamNotifier = ValueNotifier<SP20RTParamModel>(SP20RTParamModel()); final sp20RtParamNotifier = ValueNotifier<OxyRtParamModel>(OxyRtParamModel());
final sp20StatusNotifier = ValueNotifier<String>(""); final sp20StatusNotifier = ValueNotifier<String>("");
final bpDeviceDataNotifier = ValueNotifier<String>(""); final bpDeviceDataNotifier = ValueNotifier<String>("");
String deviceName = "SP20"; String deviceName = "SP20";
@ -78,7 +78,7 @@ class _SP20PulseTrackerState extends State<SP20PulseTracker> {
if (value == "EventSp20RtParam") { if (value == "EventSp20RtParam") {
return ValueListenableBuilder( return ValueListenableBuilder(
valueListenable: sp20RtParamNotifier, valueListenable: sp20RtParamNotifier,
builder: (context, SP20RTParamModel sp20RtModel, _) { builder: (context, OxyRtParamModel sp20RtModel, _) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -190,11 +190,11 @@ class _SP20PulseTrackerState extends State<SP20PulseTracker> {
} }
if (event['type'] == "EventSp20RtParam") { if (event['type'] == "EventSp20RtParam") {
sp20RtParamNotifier.value = SP20RTParamModel.fromJson(json.decode(event['data'])); sp20RtParamNotifier.value = OxyRtParamModel.fromJson(json.decode(event['data']));
sp20StatusNotifier.value = "EventSp20RtParam"; sp20StatusNotifier.value = "EventSp20RtParam";
} }
}); });
await BleChannel.getScanningResult(["Pulse Oxymeter", "SP-20"]); await BleChannel.scanResults(["Pulse Oxymeter", "SP-20"]);
}); });
} }
} }

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:diplomaticquarterapp/models/ble_devices/weight_scale_data.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_models/weight_scale_data.dart';
import 'package:diplomaticquarterapp/theme/colors.dart'; import 'package:diplomaticquarterapp/theme/colors.dart';
import 'package:diplomaticquarterapp/uitl/app_toast.dart'; import 'package:diplomaticquarterapp/uitl/app_toast.dart';
import 'package:diplomaticquarterapp/uitl/ble_utils.dart'; import 'package:diplomaticquarterapp/uitl/ble_utils.dart';

@ -28,6 +28,7 @@ import 'package:diplomaticquarterapp/pages/medical/doctor/doctor_home_page.dart'
import 'package:diplomaticquarterapp/pages/medical/eye/EyeMeasurementsPage.dart'; import 'package:diplomaticquarterapp/pages/medical/eye/EyeMeasurementsPage.dart';
import 'package:diplomaticquarterapp/pages/medical/labs/labs_home_page.dart'; import 'package:diplomaticquarterapp/pages/medical/labs/labs_home_page.dart';
import 'package:diplomaticquarterapp/pages/medical/my_invoices/my_invoice_page.dart'; import 'package:diplomaticquarterapp/pages/medical/my_invoices/my_invoice_page.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/ble_device_type_screens/select_tracker_type.dart';
import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers.dart'; import 'package:diplomaticquarterapp/pages/medical/my_trackers/my_trackers.dart';
import 'package:diplomaticquarterapp/pages/medical/patient_sick_leave_page.dart'; import 'package:diplomaticquarterapp/pages/medical/patient_sick_leave_page.dart';
import 'package:diplomaticquarterapp/pages/medical/prescriptions/prescriptions_home_page.dart'; import 'package:diplomaticquarterapp/pages/medical/prescriptions/prescriptions_home_page.dart';
@ -494,13 +495,24 @@ class Utils {
), ),
)); ));
// Oxymeter
// BloodPressure
// Blood Sugar
// ECG
// Holter
// All in one Monitor
medical.add(InkWell( medical.add(InkWell(
onTap: () => projectViewModel.havePrivilege(24) ? Navigator.push(context, FadePage(page: MyTrackers())) : null, onTap: () {
if (projectViewModel.havePrivilege(30)) {
Navigator.push(context, FadePage(page: SelectTrackerType()));
}
},
child: MedicalProfileItem( child: MedicalProfileItem(
title: "Connect", title: "Connect",
imagePath: 'tracker.svg', imagePath: 'tracker.svg',
subTitle: "BLE", subTitle: "BLE",
isEnable: projectViewModel.havePrivilege(24), isEnable: projectViewModel.havePrivilege(30),
), ),
)); ));

Loading…
Cancel
Save