# MinewMSG01Kit 说明文档
本套 SDK 仅支持 Minew 公司出品的蓝牙设备。
通过 SDK 可以帮助开发者处理手机和蓝牙设备之间的一切工作,包括:扫描设备,广播数据、连接设备,向设备写入数据,从设备接收数据等。
目前 SDK 仅支持 MSG01多合一空气质量传感器设备使用。
# 前期工作
整体框架:Msg01BleDevicesManager 为设备管理类,在 APP 运行时始终是单例。MSG01Entity 是设备实例类,此套件会为每一个设备生成一个实例,在扫描和连接后都会使用,内部包含设备广播数据,在扫描期间该数据会随着设备不停广播而更新。
Msg01BleDevicesManager :设备管理类,可以扫描周围的设备,并且可以连接它们,校验它们等
MSG01Entity :扫描时获取到的智能定位工牌设备实例,继承自 BaseBleDeviceEntity
# 一.导入到工程
# 1. 开发环境
SDK 最低支持 Android 7.0,对应 API Level 为 24 。在 module 的 build.gradle 中设置 minSdkVersion 为 24 或 24 以上 :
android {
defaultConfig {
applicationId "com.xxx.xxx"
minSdkVersion 24
}
}
2
3
4
5
6
# 2. 添加库
将 aar 包添加到 module 的 libs 文件夹下,并在该 module 的 build.gradle 中添加如下语句(直接添加依赖):
implementation files('libs\\SDK_MSG01-release.aar')
api 'org.lucee:bcprov-jdk15on:1.52.0'
api 'com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:2.16.0'
api 'com.fasterxml.jackson.core:jackson-databind:2.15.0'
2
3
4
5
添加 .so 库文件,App 目录下 build.gradle 添加如下配置:
android {
defaultConfig {
ndk {
abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'
}
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
2
3
4
5
6
7
8
9
10
11
12
# 3. 添加Ble相关权限
在AndroidManifest.xml需要以下权限,如果targetSdkVersion大于23,则需要做权限管理以获取权限:
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" tools:node="replace" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" tools:node="replace" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" tools:remove="android:usesPermissionFlags" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" tools:remove="android:usesPermissionFlags" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" tools:remove="android:usesPermissionFlags" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
2
3
4
5
6
7
8
9
10
# 二.使用
SDK 分为扫描、连接和读写三个阶段。
# 1开始扫描
Android6.0 系统以上,进行 BLE 扫描时,需要先申请到蓝牙权限后并且打开定位开关才能进行。
开启蓝牙扫描需要首先打开蓝牙,如果未打开蓝牙就去扫描,APP 会闪退。可通过 BLETool.checkBluetooth(this) 来判断蓝牙是否已经打开。如果没有打开,可以先打开蓝牙。
private fun checkBlePermissions() {
val requestPermissionList = listOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION
)
PermissionConverter.putPermissionDescriptionMap(
R.string.common_permission_nearby_devices,
R.string.common_request_gps_nearby_devices_message_permission
)
PermissionConverter.putPermissionDescriptionMap(
R.string.common_permission_location,
R.string.common_request_gps_message_permission
)
if (!XXPermissions.isGrantedPermissions(this,requestPermissionList)){
XXPermissions.with(this)
.permission(requestPermissionList)
.interceptor(PermissionInterceptor())
.description(PermissionDescription())
.request(object : OnPermissionCallback {
override fun onGranted(
permissions: MutableList<String>,
allGranted: Boolean
) {
if (!allGranted) {
return
}else{
checkBluetooth()
}
}
})
}else{
checkBluetooth()
}
}
fun checkBluetooth(){
when (BLETool.checkBluetooth(this@BusiMsg01ActivityScanDevicesActivity)) {
BluetoothState.BLE_NOT_SUPPORT -> {
Toast.makeText(this@BusiMsg01ActivityScanDevicesActivity, "Not Support BLE", Toast.LENGTH_SHORT).show()
}
BluetoothState.BLUETOOTH_OFF -> {
val enableIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
launcherActivityResultForBle.launch(enableIntent)
}
BluetoothState.BLUETOOTH_ON -> {
startScan()
}
else -> {
}
}
}
/**
* 开启扫描
*/
fun startScan(){
val manager = Msg01BleDevicesManager.getInstance()
manager.startScan(ModuleMSG01Application.getApplication().mApplication, 5*60*1000,object :
OnScanDevicesResultListener<MSG01Entity> {
override fun onScanResult(scanList: MutableList<MSG01Entity>) {
}
override fun onStopScan(scanList: MutableList<MSG01Entity>) {
}
})
}
/**
* 停止扫描
*/
private fun stopScan() {
val manager = Msg01BleDevicesManager.getInstance()
manager.stopScan(ModuleMSG01Application.getApplication().mApplication)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
注意:在扫描期间,不要频繁的调用manager.startScan()和manager.stopScan()方法。请设置合理的扫描持续时间。1分钟内避免多次调用manager.startScan()和manager.stopScan()方法时蓝牙协议栈出问题。如果出现问题,请前往手机系统设备->蓝牙开关->手动点击关闭再开启。
在扫描期间 APP 能够通过 sdk 获取到设备当前的一部分数据。如下所示通过 MSG01Entity 获取设备数据,该数据保存在广播帧对象中。
SDK 提供了 BaseBleDeviceEntity 作为 MSG01Entity 的基类,用于存储设备的公有数据,如下表所示:
| 名称 | 类型 | 说明 |
|---|---|---|
| macAddress | String | 设备mac |
| name | String | 设备名称 |
| rssi | int | 信号强度 |
BaseBleDeviceEntity 还保存了一个 BaseNanoLinkFrame,内部用于存储其在扫描期间获取到的设备广播数据帧,可通过如下方式取出:
val msg01AdvFrame : Msg01AdvFrame? = item.nanoLinkFrame?.let {it as Msg01AdvFrame}
//mac 地址
val mac = msg01AdvFrame?.macAddress
//deviceName 设备名称
String deviceName = msg01AdvFrame?.deviceName
//battery 电量百分比,默认:Integer.MIN_VALUE 表示未获取到电量
val battery = msg01AdvFrame?.battery
//固件版本号
val firmwareVersion = msg01AdvFrame?.firmwareVersion
//温度数据
val temperature = msg01AdvFrame?.temperature
//湿度数据
val humidity = msg01AdvFrame?.humidity
//co2数据
val co2= msg01AdvFrame?.co2
//PM2.5数据
val pm2_5 = msg01AdvFrame?.pm2_5
//PM1.0数据
val pm1_0 = msg01AdvFrame?.pm1_0
//PM10数据
val pm10 = msg01AdvFrame?.pm10
//tvoc数据
val voc = msg01AdvFrame?.voc
//hcho数据
val hcho = msg01AdvFrame?.hcho
//pir数据
val humanPresence = msg01AdvFrame?.humanPresence
//光强数据
val lightSensing = msg01AdvFrame?.lightSensing
//大气压数据
val atmosphericPressure = msg01AdvFrame?.atmosphericPressure
//噪音数据
val noiseSPL = msg01AdvFrame?.noiseSPL
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
MSG01多合一空气质量传感器设备有 1 种广播帧类型。
设备帧
- Msg01AdvFrame
| 名称 | 类型 | 说明 |
|---|---|---|
| mac | String | 固件mac |
| deviceName | String | 设备名称 |
| firmwareVersion | String | 版本号 |
| battery | int | 电量百分比,默认:Integer.MIN_VALUE 表示未获取到电量 |
| temperature | Temperature | 温度 private int flag; 等级:0=适宜,1=低温,2=高温 private int value; 数值 |
| humidity | Humidity | 湿度 private int flag; 等级:0=适宜,1=低湿,2=高湿 private int value; 数值 |
| co2 | Co2 | co2 private int flag; 等级:0=优,1=轻度,2=严重 private int value; 数值 |
| pm1_0 | PM1_0 | pm1.0 private int flag; 等级:0=优,1=轻度,2=严重 private int value; 数值 |
| pm2_5 | PM2_5 | pm2.5 private int flag; 等级:0=优,1=轻度,2=严重 private int value; 数值 |
| pm10 | PM10 | pm10 private int flag; 等级:0=优,1=轻度,2=严重 private int value; 数值 |
| voc | Voc | voc private int flag; 等级:0=优,1=轻度,2=严重 private int value; 数值 |
| hcho | HCHO | hcho private int flag; 等级:0=优,1=轻度,2=严重 private int value; 数值 |
| humanPresence | HumanPresence | 人员检测 private int flag; private int value; 等级:0=无人,1=有人 |
| lightSensing | LightSensing | 光强数据 private int flag; private int value; 光强等级:1、2、3、4、5 |
| atmosphericPressure | AtmosphericPressure | 大气压数据 private int flag; private int value; |
| noiseSPL | NoiseSPL | 噪音数据 private int flag; private int value; |
# 2连接
连接前一般需要先停止扫描 ,SDK 提供了连接和断开连接方法。
val mBleManager = Msg01BleDevicesManager.getInstance()
//停止扫描
mBleManager.stopScan(context);
//连接 : module 为准备连接的设备
val module:MSG01Entity;
mBleManager.connect(context,module)
//断开连接 :macAddress 为设备 mac
mBleManager.disConnect(macAddress)
2
3
4
5
6
7
8
注意:连接设备前,请确认是否扫描到设备,如果没扫描到设备广播,调用连接方法,将连接失败。
在调用 connect() 后,SDK 中会对连接过程会有状态监听。
//设置监听器
mBleManager.setOnConnStateListener {macAddress, connectionState ->
when (it) {
BleConnectionState.Connecting -> {
//调用connect()后就会回调该状态
}
BleConnectionState.Connected -> {
//初步连接成功,作为一个过渡阶段,此时并未真正成功
}
BleConnectionState.EnterAuthenticatePassword -> {
}
BleConnectionState.AuthenticateSuccess ->{
}
BleConnectionState.AuthenticateFail ->{
}
BleConnectionState.ConnectComplete -> {
//设备连接完成,才能通过 Msg01BleDevicesManager 中提供的方法操作设备或者跳转界面
}
BleConnectionState.Bond_None -> {
//设备配对回调,此处为无效配对状态
}
BleConnectionState.Bond_Bonding -> {
//设备配对回调,此处为正在配对状态
}
BleConnectionState.Bond_Bonded -> {
//设备配对回调,此处为配对完成状态
//设备配对完成,才能通过 Msg01BleDevicesManager 中提供的方法操作设备
}
BleConnectionState.Disconnect -> {
LogUtil.d("connectionListener", "ConnectionState.Disconnect")
LoadingDialogUtil.dismissLoadingDialog()
// ToastUtils.showLong(getString(R.string.conn_failure))
}
else -> {}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
在连接过程中,sdk会返回多个连接状态到app中,app需要做好处理。
- BleConnectionState.Connecting,BleConnectionState.Connected: 连接设备中,在这里状态下不要做耗时操作,因为此时在连接设备发现服务,并发送认证数据等。
- BleConnectionState.Bond_None: 设备配对无效状态。
- BleConnectionState.Bond_Bonding: 设备正在配对状态。
- BleConnectionState.Bond_Bonded: 设备配对完成状态,才能通过 Msg01BleDevicesManager中提供的方法操作设备。
- BleConnectionState.ConnectComplete: 设备已经连接成功,可以进行读写操作,比如配置广播参数、读取历史数据等。
- BleConnectionState.Disconnect: 连接失败或者设备断开连接会回调。
# 3设备配置读取写入操作
设备配置读取写入操作 API 如下,使用 Msg01BleDevicesManager.getInstance() 对象调用完成:
# 1. 给设备授予手机当前时间
默认SDK在连接设备阶段会自动给设备授予手机时间
// 首先要检查蓝牙开关是否打开,只有在打开的情况下才能开启手机的 CurrentTimeService 服务,如果蓝牙开关关闭去开启,部分 Android 手机系统可能会出错。并且需要监听蓝牙开关状态,当蓝牙开关开启时候需要开启服务 startSyncTimeServer,关闭的时候需要关闭服务 closeSyncTimeServer。注意,当 activity 页面 ondestroy 时,也需要关闭该服务 closeSyncTimeServer
val mBleManager = Msg01BleDevicesManager.getInstance()
// 开启服务
mBleManager.startSyncTimeServer(context)
// 关闭服务
mBleManager.closeSyncTimeServer(context)
// 因为部分手机会偶现授时失败的情况,所以需要判断授时是否成功,建议在设备连接成功 3 秒后判断
mBleManager.isDeviceReadSyncTime(context);
// 如果授时操作不成功,那么需要主动给设备发送授时指令
mBleManager.writeTimeToDevice(macAddress, onWriteTimeToDeviceListener);
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 2. 获取固件版本
FirmwareVersionModel
| 名称 | 类型 | 说明 |
|---|---|---|
| versionInfoList | List | 版本信息集合 |
VersionInfo
| 名称 | 类型 | 说明 |
|---|---|---|
| firmwareName | String | 固件名称 |
| firmwareType | int | 默认值-1 |
| firmwareVersion | String | 固件版本号 |
| slot | String | 插槽, |
| methods | ArrayList |
/**
* 获取固件版本
* @param macAddress 设备mac
* @param listener 监听器
*/
void getFirmwareVersion(@NonNull String macAddress, OnQueryResultListener<FirmwareVersionModel> listener);
/**
* 获取固件版本信息
* @return FirmwareVersionModel?
*/
suspend fun getFirmwareInfo(): FirmwareVersionModel? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<FirmwareVersionModel?> { continuation ->
manager.getFirmwareVersion(connectMacAddress!!,
OnQueryResultListener<FirmwareVersionModel> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 获取版本信息
*/
private fun getFirmwareInfo(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getFirmwareInfo()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
val firmwareVersion = it.versionInfoList.first()
binding.tvFirmwareVersion.text = "V${firmwareVersion.firmwareVersion}"
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 3. 恢复出厂设置
/**
* String macAddress
* 恢复出厂设置
*
* @param macAddress 设备mac
* @param listener 监听器
*/
void reset(@NonNull String macAddress, OnModifyConfigurationListener listener);
/**
* 恢复出厂设置
* @return Boolean
*/
suspend fun reset(): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.reset(connectMacAddress!!) {
continuation.resume(it, null)
}
}
}
/**
* 恢复出厂设置
*/
private fun reset(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.reset()
LoadingDialogUtil.dismissLoadingDialog()
ToastUtils.showShort(when(result){
true -> R.string.common_reset_success
else -> R.string.common_reset_failure
})
mConnectedViewModel.disconnect(mConnectedViewModel.connectMacAddress!!)
finish()
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 4. 关机
/**
* 关机
*
* @param macAddress 设备mac
* @param listener 监听器
*/
void powerOff(@NonNull String macAddress, OnModifyConfigurationListener listener);
/**
* 关机
* @return Boolean
*/
suspend fun shutdown(): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.powerOff(connectMacAddress!!) {
continuation.resume(it, null)
}
}
}
/**
* 关机
*/
private fun powerOff(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.shutdown()
LoadingDialogUtil.dismissLoadingDialog()
ToastUtils.showShort(when(result){
true -> R.string.common_power_off_success
else -> R.string.common_power_off_failure
})
mConnectedViewModel.disconnect(mConnectedViewModel.connectMacAddress!!)
finish()
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 5. 设置静态密码
/**
* 设置静态密码
* @param macAddress mac地址
* @param passkey 密码 6-digit and the range is 000000 - 999999
* @param listener 监听器
*/
void setStaticPassKey(@NonNull String macAddress, int passkey, OnModifyConfigurationListener listener);
/**
* 修改设备密码
* @return Boolean
*/
suspend fun setPasskey(password:Int): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.setStaticPassKey(connectMacAddress!!,password) {
continuation.resume(it, null)
}
}
}
/**
* 设置设备密码
* @param password String
*/
private fun setDevicePassword(password:Int){
lifecycleScope.launch(Dispatchers.Main) {
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setPasskey(password)
LoadingDialogUtil.dismissLoadingDialog()
ToastUtils.showShort(when(result){
true -> R.string.busi_msg01_set_device_password_success_message
else -> R.string.busi_msg01_set_device_password_fail_message
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 6. 查询和设置Co2传感器参数
Co2SensorConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 采样间隔,单位s |
| lim | int[] | co2阈值范围。index=0轻度的阈值,index=1严重阈值 |
| abc | Abc | co2传感器配置 |
Abc
| 名称 | 类型 | 描述 |
|---|---|---|
| en | boolean | 是否开启co2传感器,true开启。false不开启 |
| intvl | int | 运行频率,一般用于零点自校准。范用1~30天, |
/**
* 查询Co2传感器参数
* @param macAddress
* @param listener
*/
void getCo2SensorConfig(@NonNull String macAddress, OnQueryResultListener<Co2SensorConfigEntity> listener);
/**
* 设置Co2传感器参数
* @param macAddress
* @param co2SensorConfigEntity
* @param listener
*/
void setCo2SensorConfig(@NonNull String macAddress, Co2SensorConfigEntity co2SensorConfigEntity, OnModifyConfigurationListener listener);
/**
* 获取co2 传感器配置
* @return Co2SensorConfigEntity?
*/
suspend fun getCo2SensorConfig(): Co2SensorConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Co2SensorConfigEntity?> { continuation ->
manager.getCo2SensorConfig(connectMacAddress!!,
OnQueryResultListener<Co2SensorConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
*设置co2传感器配置
*/
suspend fun setCo2SensorConfig(co2SensorConfigEntity: Co2SensorConfigEntity): Boolean? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Boolean?> { continuation ->
manager.setCo2SensorConfig(connectMacAddress!!,co2SensorConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 获取co2传感器参数
*/
private fun getCo2SensorConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getCo2SensorConfig()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
LogUtil.d("Co2SensorConfig=${it.toString()}")
binding.co2SamplingIntervalTv.text = "${DataUtil.getHour(it.intvl)}h ${DataUtil.getMinute(it.intvl)}m ${DataUtil.getSecond(it.intvl)}s"
when(it.lim != null){
true ->{
when(it.lim.size){
1 -> {
binding.co2GoodSensorThresholdTv.setText("${it.lim[0]}")
binding.co2MildSensorThresholdEdit.setText("${it.lim[0]}")
binding.co2SevereSensorThresholdEdit.setText("")
}
2 ->{
binding.co2GoodSensorThresholdTv.setText("${it.lim[0]}")
binding.co2MildSensorThresholdEdit.setText("${it.lim[0]}")
binding.co2SevereSensorThresholdEdit.setText("${it.lim[1]}")
}
else ->{}
}
binding.switchCo2.isChecked = true
binding.co2AlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.co2GoodSensorThresholdTv.setText("")
binding.co2MildSensorThresholdEdit.setText("")
binding.co2SevereSensorThresholdEdit.setText("")
binding.switchCo2.isChecked = false
binding.co2AlarmThresholdContentLayout.visibility = View.GONE
}
}
}
}
}
/**
* 设置co2传感器参数
*/
private fun setCo2SensorConfig(co2SensorConfigEntity: Co2SensorConfigEntity){
lifecycleScope.launch(Dispatchers.Main){
LogUtil.d("setCo2SensorConfig:${co2SensorConfigEntity.toString()}")
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setCo2SensorConfig(co2SensorConfigEntity)
LoadingDialogUtil.dismissLoadingDialog()
when(result){
true ->ToastUtils.showShort(getString(R.string.common_config_success))
else ->ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# 7. 设置CO2传感器单点校准参数
Co2DioxideSensorSinglePointConfig
| 名称 | 类型 | 描述 |
|---|---|---|
| ref | int | 有效范围400 ~ 1500 ppm. |
/**
* 设置CO2传感器参数
* @param macAddress
* @param carbonDioxideSensorConfiguration
* @param listener
*/
void setCo2DioxideSensorSinglePointConfiguration(@NonNull String macAddress, Co2DioxideSensorSinglePointConfig carbonDioxideSensorConfiguration, OnModifyConfigurationListener listener);
/**
* 设置 CO2 单点自校准
* @param enable Boolean 是否开启单点自校准
* @param periodDay Int 运行频率,单位天,范围 1~30
* @return Boolean
*/
suspend fun setCo2DioxideSensorConfig(co2DioxideSensorConfig: Co2DioxideSensorSinglePointConfig): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.setCo2DioxideSensorSinglePointConfiguration(connectMacAddress!!,co2DioxideSensorConfig) {
continuation.resume(it, null)
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 8. 查询和设置Voc传感器参数
VocSensorConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 采样间隔,单位s |
| lim | int[] | voc阈值范围。index=0轻度的阈值,index=1严重阈值 |
/**
* 设置Voc传感器参数
* @param macAddress
* @param listener
*/
void setVocSensorConfig(@NonNull String macAddress, VocSensorConfigEntity vocSensorConfigEntity, OnModifyConfigurationListener listener);
/**
* 查询Voc传感器参数
* @param macAddress
* @param listener
*/
void getVocSensorConfig(@NonNull String macAddress, OnQueryResultListener<VocSensorConfigEntity> listener);
/**
* 获取voc 传感器配置
* @return VocSensorConfigEntity?
*/
suspend fun getTvocSensorConfig(): VocSensorConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<VocSensorConfigEntity?> { continuation ->
manager.getVocSensorConfig(connectMacAddress!!,
OnQueryResultListener<VocSensorConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置voc传感器配置
*/
suspend fun setTvocSensorConfig(vocSensorConfigEntity: VocSensorConfigEntity): Boolean? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Boolean?> { continuation ->
manager.setVocSensorConfig(connectMacAddress!!,vocSensorConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 获取 tvoc传感器参数
*/
private fun getTvocSensorConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getTvocSensorConfig()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
LogUtil.d("TvocSensorConfig=${it.toString()}")
binding.tvocSamplingIntervalTv.text = "${DataUtil.getHour(it.intvl)}h ${DataUtil.getMinute(it.intvl)}m"
when(it.lim != null){
true ->{
when(it.lim.size){
1 ->{
binding.tvocGoodSensorThresholdTv.setText("${it.lim[0]}")
binding.tvocMildSensorThresholdEdit.setText("${it.lim[0]}")
binding.tvocSevereSensorThresholdEdit.setText("")
}
2 ->{
binding.tvocGoodSensorThresholdTv.setText("${it.lim[0]}")
binding.tvocMildSensorThresholdEdit.setText("${it.lim[0]}")
binding.tvocSevereSensorThresholdEdit.setText("${it.lim[1]}")
}
else ->{}
}
binding.switchTvoc.isChecked = true
binding.tvocAlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.tvocGoodSensorThresholdTv.setText("")
binding.tvocMildSensorThresholdEdit.setText("")
binding.tvocSevereSensorThresholdEdit.setText("")
binding.switchTvoc.isChecked = false
binding.tvocAlarmThresholdContentLayout.visibility = View.GONE
}
}
}
}
}
/**
* 设置 tvoc传感器参数
*/
private fun setTvocSensorConfig(vocSensorConfigEntity: VocSensorConfigEntity){
lifecycleScope.launch(Dispatchers.Main){
LogUtil.d("setTvocSensorConfig:${vocSensorConfigEntity.toString()}")
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setTvocSensorConfig(vocSensorConfigEntity)
LoadingDialogUtil.dismissLoadingDialog()
when(result){
true ->ToastUtils.showShort(getString(R.string.common_config_success))
else ->ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# 9. 查询和设置Pm传感器参数
PmSensorConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 采样间隔,单位s |
| pm1_0 | int[] | pm1.0阈值范围。index=0轻度的阈值,index=1严重阈值 |
| pm2_5 | int[] | pm2.5阈值范围。index=0轻度的阈值,index=1严重阈值 |
| pm10 | int[] | pm10阈值范围。index=0轻度的阈值,index=1严重阈值 |
/**
* 查询Pm传感器参数
* @param macAddress
* @param listener
*/
void getPmSensorConfig(@NonNull String macAddress, OnQueryResultListener<PmSensorConfigEntity> listener);
/**
* 设置Pm传感器参数
* @param macAddress
* @param pmSensorConfig
* @param listener
*/
void setPmSensorConfig(@NonNull String macAddress, PmSensorConfigEntity pmSensorConfig, OnModifyConfigurationListener listener);
/**
* 获取PM 传感器配置
* @return PmSensorConfigEntity?
*/
suspend fun getPmSensorConfig(): PmSensorConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<PmSensorConfigEntity?> { continuation ->
manager.getPmSensorConfig(connectMacAddress!!,
OnQueryResultListener<PmSensorConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置PM传感器配置
*/
suspend fun setPmSensorConfig(pmSensorConfigEntity: PmSensorConfigEntity): Boolean? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Boolean?> { continuation ->
manager.setPmSensorConfig(connectMacAddress!!,pmSensorConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 获取pm传感器参数
*/
private fun getPmSensorConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getPmSensorConfig()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
LogUtil.d("PmSensorConfig=${it.toString()}")
binding.pm25SamplingIntervalTv.text = "${DataUtil.getHour(it.intvl)}h ${DataUtil.getMinute(it.intvl)}m"
when(it.pm2_5 != null){
true ->{
when(it.pm2_5.size){
1 ->{
binding.pm25GoodSensorThresholdTv.setText("${it.pm2_5[0]}")
binding.pm25MildSensorThresholdEdit.setText("${it.pm2_5[0]}")
binding.pm25SevereSensorThresholdEdit.setText("")
}
2 ->{
binding.pm25GoodSensorThresholdTv.setText("${it.pm2_5[0]}")
binding.pm25MildSensorThresholdEdit.setText("${it.pm2_5[0]}")
binding.pm25SevereSensorThresholdEdit.setText("${it.pm2_5[1]}")
}
else ->{}
}
binding.switchPm25.isChecked = true
binding.pm25AlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.pm25GoodSensorThresholdTv.setText("")
binding.pm25MildSensorThresholdEdit.setText("")
binding.pm25SevereSensorThresholdEdit.setText("")
binding.switchPm25.isChecked = false
binding.pm25AlarmThresholdContentLayout.visibility = View.GONE
}
}
when(it.pm10 != null){
true ->{
when(it.pm10.size){
1 ->{
binding.pm10GoodSensorThresholdTv.setText("${it.pm10[0]}")
binding.pm10MildSensorThresholdEdit.setText("${it.pm10[0]}")
binding.pm10SevereSensorThresholdEdit.setText("")
}
2 ->{
binding.pm10GoodSensorThresholdTv.setText("${it.pm10[0]}")
binding.pm10MildSensorThresholdEdit.setText("${it.pm10[0]}")
binding.pm10SevereSensorThresholdEdit.setText("${it.pm10[1]}")
}
else ->{}
}
binding.switchPm10.isChecked = true
binding.pm10AlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.pm10GoodSensorThresholdTv.setText("")
binding.pm10MildSensorThresholdEdit.setText("")
binding.pm10SevereSensorThresholdEdit.setText("")
binding.switchPm10.isChecked = false
binding.pm10AlarmThresholdContentLayout.visibility = View.GONE
}
}
}
}
}
/**
* 设置 pm传感器参数
*/
private fun setPmSensorConfig(pmSensorConfigEntity: PmSensorConfigEntity){
lifecycleScope.launch(Dispatchers.Main){
LogUtil.d("setPmSensorConfig:${pmSensorConfigEntity.toString()}")
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setPmSensorConfig(pmSensorConfigEntity)
LoadingDialogUtil.dismissLoadingDialog()
when(result){
true ->ToastUtils.showShort(getString(R.string.common_config_success))
else ->ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# 10. 查询和设置hoco传感器参数
HCHOSensorConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 采样间隔,单位s |
| lim | int[] | 阈值范围。index=0轻度的阈值,index=1严重阈值 |
/**
* 查询hoco传感器参数
* @param macAddress
* @param listener
*/
void getHchoSensorConfig(@NonNull String macAddress, OnQueryResultListener<HCHOSensorConfigEntity> listener);
/**
* 设置hoco传感器参数
* @param macAddress
* @param hchoSensorConfig
* @param listener
*/
void setHchoSensorConfig(@NonNull String macAddress, HCHOSensorConfigEntity hchoSensorConfig, OnModifyConfigurationListener listener);
/**
* 获取hcho 传感器配置
* @return HCHOSensorConfigEntity?
*/
suspend fun getHchoSensorConfig(): HCHOSensorConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<HCHOSensorConfigEntity?> { continuation ->
manager.getHchoSensorConfig(connectMacAddress!!,
OnQueryResultListener<HCHOSensorConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置hcho传感器配置
*/
suspend fun setHchoSensorConfig(hchoSensorConfigEntity: HCHOSensorConfigEntity): Boolean? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Boolean?> { continuation ->
manager.setHchoSensorConfig(connectMacAddress!!,hchoSensorConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 获取 hcho传感器参数
*/
private fun getHchoSensorConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getHchoSensorConfig()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
LogUtil.d("HchoSensorConfig=${it.toString()}")
binding.hchoSamplingIntervalTv.text = "${DataUtil.getForDayInDay(it.intvl)}d ${DataUtil.getForDayInHour(it.intvl)}h"
when(it.lim != null ){
true ->{
when(it.lim.size){
1 ->{
binding.hchoGoodSensorThresholdTv.setText("${BigDecimal(it.lim[0]).divide(
BigDecimal(1000)).setScale(3).toDouble()}")
binding.hchoMildSensorThresholdEdit.setText("${BigDecimal(it.lim[0]).divide(
BigDecimal(1000)).setScale(3).toDouble()}")
binding.hchoSevereSensorThresholdEdit.setText("")
}
2 ->{
binding.hchoGoodSensorThresholdTv.setText("${BigDecimal(it.lim[0]).divide(
BigDecimal(1000)).setScale(3).toDouble()}")
binding.hchoMildSensorThresholdEdit.setText("${BigDecimal(it.lim[0]).divide(
BigDecimal(1000)).setScale(3).toDouble()}")
binding.hchoSevereSensorThresholdEdit.setText("${BigDecimal(it.lim[1]).divide(
BigDecimal(1000)).setScale(3).toDouble()}")
}
else ->{}
}
binding.switchHcho.isChecked = true
binding.hchoAlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.hchoGoodSensorThresholdTv.setText("")
binding.hchoMildSensorThresholdEdit.setText("")
binding.hchoSevereSensorThresholdEdit.setText("")
binding.switchHcho.isChecked = false
binding.hchoAlarmThresholdContentLayout.visibility = View.GONE
}
}
}
}
}
/**
* 设置 hcho传感器参数
*/
private fun setHchoSensorConfig(hchoSensorConfigEntity: HCHOSensorConfigEntity){
lifecycleScope.launch(Dispatchers.Main){
LogUtil.d("setHchoSensorConfig:${hchoSensorConfigEntity.toString()}")
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setHchoSensorConfig(hchoSensorConfigEntity)
LoadingDialogUtil.dismissLoadingDialog()
when(result){
true ->ToastUtils.showShort(getString(R.string.common_config_success))
else ->ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# 11. 查询ht传感器参数
HTSensorConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 温湿度采样间隔。单位秒 |
| t_ref | HtRef | 温度阈值参数,t_ref=null,表示没启用温度采集 |
| h_ref | HtRef | 湿度阈值参数,h_ref=null,表示没启用湿度采集 |
HtRef
| 名称 | 类型 | 描述 |
|---|---|---|
| min | int | 阈值最小值,默认值=Integer.MIN_VALUE,格式是x100,显示需要value/100f使用。配置需要x100去设置 |
| max | int | 阈值最大值,默认值=Integer.MIN_VALUE,格式是x100,显示需要value/100f使用。配置需要x100去设置 |
/**
* 查询ht传感器参数
* @param macAddress
* @param listener
*/
void getHtSensorConfig(@NonNull String macAddress, OnQueryResultListener<HTSensorConfigEntity> listener);
/**
* 设置ht传感器参数
* @param macAddress
* @param htSensorConfig
* @param listener
*/
void setHtSensorConfig(@NonNull String macAddress, HTSensorConfigEntity htSensorConfig, OnModifyConfigurationListener listener);
/**
* 获取HT 传感器配置
* @return HCHOSensorConfigEntity?
*/
suspend fun getHTSensorConfig(): HTSensorConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<HTSensorConfigEntity?> { continuation ->
manager.getHtSensorConfig(connectMacAddress!!,
OnQueryResultListener<HTSensorConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置ht 传感器配置
* @return Boolean?
*/
suspend fun setHTSensorConfig(htSensorConfigEntity: HTSensorConfigEntity): Boolean? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Boolean?> { continuation ->
manager.setHtSensorConfig(connectMacAddress!!,htSensorConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 获取 HT传感器参数
*/
private fun getHTSensorConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getHTSensorConfig()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
binding.htSamplingIntervalTv.text = "${DataUtil.getHour(it.intvl)}h ${DataUtil.getMinute(it.intvl)}m ${DataUtil.getSecond(it.intvl)}s"
when(it.t_ref != null){
true ->{
if(it.t_ref?.min != Int.MIN_VALUE){
binding.temperatureLowSensorThresholdEdit.setText("${(it.t_ref?.min?:0)/100f}")
}
if(it.t_ref?.max != Int.MIN_VALUE){
binding.temperatureHighSensorThresholdEdit.setText("${(it.t_ref?.max?:45)/100f}")
}
binding.switchTemperature.isChecked = true
binding.temperatureAlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.switchTemperature.isChecked = false
binding.temperatureAlarmThresholdContentLayout.visibility = View.GONE
}
}
when(it.h_ref != null){
true ->{
if(it.h_ref?.min != Int.MIN_VALUE){
binding.humidityLowSensorThresholdEdit.setText("${(it.h_ref?.min?:0)/100f}")
}
if(it.h_ref?.max != Int.MIN_VALUE){
binding.humidityHighSensorThresholdEdit.setText("${(it.h_ref?.max?:45)/100f}")
}
binding.switchHumidity.isChecked = true
binding.humidityAlarmThresholdContentLayout.visibility = View.VISIBLE
}
else ->{
binding.switchHumidity.isChecked = false
binding.humidityAlarmThresholdContentLayout.visibility = View.GONE
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 12. 查询和设置广播参数
AdvConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 广播间隔,范围100ms ~ 10000ms ,刻度为 100ms |
| txpwr | int | 广播功率,广播功率有8个:-40dBm、-20dBm、-16dBm、-12dBm、-8dBm、-4dBm、0dBm、4dBm、8dBm |
/**
* 读取广播参数
* @param macAddress
* @param listener
*/
void getAdvConfig(@NonNull String macAddress, OnQueryResultListener<AdvConfigEntity> listener);
/**
* 设置广播参数
* @param macAddress
* @param advConfigEntity
* @param listener
*/
void setAdvConfig(@NonNull String macAddress, AdvConfigEntity advConfigEntity, OnModifyConfigurationListener listener);
/**
* 获取广播参数
* @return AdvConfigEntity?
*/
suspend fun getAdvParams(): AdvConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<AdvConfigEntity?> { continuation ->
manager.getAdvConfig(connectMacAddress!!,
OnQueryResultListener<AdvConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置广播参数
* @return Boolean
*/
suspend fun setAdvParams(advConfigEntity: AdvConfigEntity): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.setAdvConfig(connectMacAddress!!,advConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 查询广播参数
*/
private fun getAdvConfiguration() {
lifecycleScope.launch(Dispatchers.Main) {
LoadingDialogUtil.showLoadingDialog()
val advParams =mConnectedViewModel.getAdvParams()
LoadingDialogUtil.dismissLoadingDialog()
advParams?.let {
mAdvConfigEntity = it
binding.advIntervalEdit.setText("${it.intvl}")
binding.advIntervalEdit.setSelection(binding.advIntervalEdit.text.toString().length)
binding.advIntervalSeekbar.progress =
intervalRange.indexOf(it.intvl / intervalStep)
binding.advTxPowerTv.text = "${it.txpwr} dBm"
binding.advTxPowerSeekbar.progress = powerRange.indexOf(it.txpwr)
}
}
}
/**
* 设置中继帧帧广播参数
*/
private fun setAdvertisingParametersConfiguration() {
mAdvConfigEntity?.let {
lifecycleScope.launch(Dispatchers.Main) {
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setAdvParams(it)
LoadingDialogUtil.dismissLoadingDialog()
when(result){
true ->ToastUtils.showShort(getString(R.string.common_config_success))
else ->ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# 13. 读取和设置告警灯效果参数
AlarmLightEffectConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| en | boolean | true=打开,false=关闭 |
/**
* 读取告警灯效果参数
* @param macAddress
* @param listener
*/
void getAlarmLightEffectConfig(@NonNull String macAddress, OnQueryResultListener<AlarmLightEffectConfigEntity> listener);
/**
* 设置告警灯效果参数
* @param macAddress
* @param alarmLightEffectConfig
* @param listener
*/
void setAlarmLightEffectConfig(@NonNull String macAddress, AlarmLightEffectConfigEntity alarmLightEffectConfig, OnModifyConfigurationListener listener);
/**
* 查询报警灯效配置
* @return AlarmLightEffectConfigEntity?
*/
suspend fun getAlarmLightEffect(): AlarmLightEffectConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<AlarmLightEffectConfigEntity?> { continuation ->
manager.getAlarmLightEffectConfig(connectMacAddress!!,
OnQueryResultListener<AlarmLightEffectConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置报警灯效配置
* @return Boolean
*/
suspend fun setAlarmLightEffect(alarmLightEffectConfigEntity: AlarmLightEffectConfigEntity): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.setAlarmLightEffectConfig(connectMacAddress!!,alarmLightEffectConfigEntity) {
continuation.resume(it, null)
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 14. 查询光感传感器参数
LightSensorConfigEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| intvl | int | 采样间隔。单位秒 |
/**
* 查询光感传感器参数
* @param macAddress
* @param listener
*/
void getLightSensorConfig(@NonNull String macAddress, OnQueryResultListener<LightSensorConfigEntity> listener);
/**
* 设置光感传感器参数
* @param macAddress
* @param lightSensorConfig
* @param listener
*/
void setLightSensorConfig(@NonNull String macAddress, LightSensorConfigEntity lightSensorConfig, OnModifyConfigurationListener listener);
/**
* 获取光传感器配置
* @return LightSensorConfigEntity?
*/
suspend fun getLightSensorConfig(): LightSensorConfigEntity? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<LightSensorConfigEntity?> { continuation ->
manager.getLightSensorConfig(connectMacAddress!!,
OnQueryResultListener<LightSensorConfigEntity> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置光传感器配置
* @return lightSensorConfigEntity?
*/
suspend fun setLightSensorConfig(lightSensorConfigEntity: LightSensorConfigEntity): Boolean? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<Boolean?> { continuation ->
manager.setLightSensorConfig(connectMacAddress!!,lightSensorConfigEntity) {
continuation.resume(it, null)
}
}
}
/**
* 获取 Light传感器参数
*/
private fun getLightSensorConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getLightSensorConfig()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
LogUtil.d("LightSensorConfig=${it.toString()}")
mLightSensorConfig = it
mLightSamplingInterval = it.intvl
binding.lightSensorSamplingIntervalTv.text = "${DataUtil.getHour(it.intvl)}h ${DataUtil.getMinute(it.intvl)}m ${DataUtil.getSecond(it.intvl)}s"
}
}
}
/**
* 设置 Light传感器参数
*/
private fun setLightSensorConfig(lightSensorConfigEntity: LightSensorConfigEntity){
lifecycleScope.launch(Dispatchers.Main){
LogUtil.d("setLightSensorConfig:${lightSensorConfigEntity.toString()}")
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setLightSensorConfig(lightSensorConfigEntity)
LoadingDialogUtil.dismissLoadingDialog()
when(result){
true ->ToastUtils.showShort(getString(R.string.common_config_success))
else ->ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 15. 查询显示语言参数
DisplayLanguageConfig
| 名称 | 类型 | 描述 |
|---|---|---|
| lang | String | 语言。中文="zh-CN",英语="en" |
/**
* 查询显示语言参数
* @param macAddress
* @param listener
*/
void getDisplayLanguageConfig(@NonNull String macAddress, OnQueryResultListener<DisplayLanguageConfig> listener);
/**
* 设置显示语言参数
* @param macAddress
* @param displayLanguageConfig
* @param listener
*/
void setDisplayLanguageConfig(@NonNull String macAddress, DisplayLanguageConfig displayLanguageConfig, OnModifyConfigurationListener listener);
/**
* 获取设备屏幕显示语言
* @return DisplayLanguageConfig?
*/
suspend fun getScreenLanguage(): DisplayLanguageConfig? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<DisplayLanguageConfig?> { continuation ->
manager.getDisplayLanguageConfig(connectMacAddress!!,
OnQueryResultListener<DisplayLanguageConfig> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
/**
* 设置设备屏幕显示语言
* @param language
* @return Boolean
*
*/
suspend fun setScreenLanguage(language: DisplayLanguageConfig): Boolean = withContext(Dispatchers.Default) {
if (connectMacAddress == null) {
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.setDisplayLanguageConfig(connectMacAddress!!,language) {
continuation.resume(it, null)
}
}
}
/**
* 获取屏幕语言
*/
private fun getDisplayLanguageConfig(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.getScreenLanguage()
LoadingDialogUtil.dismissLoadingDialog()
result?.let {
when (it.lang) {
LANGUAGE_CHINESE -> {
selectLanguage(LANGUAGE_CHINESE)
}
LANGUAGE_ENGLISH -> {
selectLanguage(LANGUAGE_ENGLISH)
}
else -> {
selectLanguage(LANGUAGE_CHINESE)
}
}
}
}
}
/**
* 设置屏幕语言
*/
private fun setScreenLanguage() {
val config = (displayLanguageConfig?:DisplayLanguageConfig()).apply {
this.lang = mSelectedLanguage
}
lifecycleScope.launch(Dispatchers.Main) {
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.setScreenLanguage(config)
LoadingDialogUtil.dismissLoadingDialog()
when (result) {
true -> ToastUtils.showShort(getString(R.string.common_config_success))
else -> ToastUtils.showShort(getString(R.string.common_config_fail))
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 16. 固件升级。
/**
* 固件升级
* @param context 上下文
* @param macAddress mac地址
* @param isLinkUpgrade 是否连接升级还是OTA升级 true:url升级 ,false:OTA升级
* @param filePath OTA文件路径,url升级时可为null
* @param upgradeData OTA数据,url升级时可为null
* @param target 升级固件目标,OTA数据,默认 target="main",url升级时为target="app"
* @param listener
*/
void firmwareUpgrade(@NonNull Context context,@NonNull String macAddress, boolean isLinkUpgrade, String filePath, @NonNull byte[] upgradeData, String target, OnFirmwareUpgradeListener listener) ;
/**
* 调制到系统文件
*/
private fun gotoSystemFilePage() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.type = "*/*"
launcherActivityResult.launch(intent)
}
//读取固件升级回调
private val launcherActivityResult =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val fileUri = it.data?.data
LogUtil.e("fileUri","fileUri=${fileUri.toString()}")
fileUri?.let { uri ->
handleFileByQ(uri)
}
}
}
private fun handleFile(fileUri: Uri) {
//获取压缩包文件名
var fileName = ""
//获取压缩包路径 uri转path
val cursor =this.contentResolver.query(fileUri, null, null, null, null, null)
try {
if (cursor != null && cursor.moveToFirst()) {
fileName = cursor.getString(
cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
)
} else {
}
} finally {
cursor?.close()
}
val zipFilePath = FileUtil.getFileAbsolutePath(this, fileUri)
toFirmwareUpgradePage(zipFilePath)
}
/**
* 解析升级包
* @param filePath String
*/
private fun dealFileData(filePath: String) {
MinewExecutors.getInstance().getDiskIO().execute {
try {
val bininputstream = ZipUtil.readZipFile(filePath, ".bin")
val bufferedInputStream = BufferedInputStream(bininputstream)
val allData = ArrayList<Byte>()
//每次读取4k
val buffer = ByteArray(1024 * 4)
var bytesRead = 0
var tempLength = 0
while (bufferedInputStream.read(buffer).also { bytesRead = it } != -1) {
for (i in 0 until bytesRead) {
val by = buffer[i]
allData.add(by)
}
tempLength += bytesRead
}
val fileData = ByteArray(allData.size)
for (i in allData.indices) {
fileData[i] = allData[i]
}
bufferedInputStream.close()
//调用升级指令
firmwareUpgrade(this@FirmwareUpgradeActivity,filePath,fileData, "main")
} catch (e: FileNotFoundException) {
e.printStackTrace()
chooseFileError(R.string.common_file_select_error)
} catch (e: IOException) {
e.printStackTrace()
chooseFileError(R.string.common_file_select_error)
}
}
}
/**
* 固件升级
*/
private fun firmwareUpgrade(
context: Context,
filePath:String,
fileByte: ByteArray,
target: String
) {
mConnectedViewModel.firmwareUpgrade(context,filePath, fileByte,target,
progressCallBack = { progress ->
WaitDialog.show(getString(R.string.common_upgrading), (progress / 100f))
binding.progressBarSimpleCustom.progress = progress.toFloat()
},
successCallBack = {
WaitDialog.dismiss()
ToastUtils.showShort(R.string.common_firmware_upgrade_successfully)
},
failCallBack = {
WaitDialog.dismiss()
ToastUtils.showShort(R.string.common_firmware_upgrade_failure2)
}
)
}
/**
* 固件升级
*/
fun firmwareUpgrade(
context: Context,
filePath:String,
fileByte: ByteArray,
target:String,
progressCallBack:(progress:Int) -> Unit,
successCallBack:() -> Unit,
failCallBack:() -> Unit,
){
manager.firmwareUpgrade(context,connectMacAddress!!,false,filePath,fileByte,target,
object : OnFirmwareUpgradeListener {
override fun updateProgress(progress: Int) {
//升级包数据写入进度
progressCallBack(progress)
}
override fun upgradeSuccess() {
// 升级成功回调,此时设备会主动跟手机断开连接,所以会触发OnConnStateListener回调
successCallBack()
}
override fun upgradeFailed() {
//升级失败
failCallBack()
}
})
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# 17. 查询历史记录
历史数据可以查询Co2、激光粉尘、voc、温湿度、HCHO等5种类型数据。
/**
* 获取设备存储数据
* @param queryAllData true=查询全部,false=按时间段查询。注意:建议按时间段查询,查询全部可能受限制手机运行内存导致查询失败。
* @param startTime Long 开始查询时间。单位秒
* @param endTime Long 结束查询时间。单位秒
* @param systemTime Long 手机系统当前时间,单位秒。建议直接传:System.currentTimeMillis()/1000
*/
private fun getDeviceStorageData(queryAllData:Boolean, sensorType: StorageDataBlockType, startTime:Long, endTime:Long, systemTime: Long){
lifecycleScope.launch(Dispatchers.Main) {
LoadingDialogUtil.showLoadingDialog()
lifecycleScope.launch(Dispatchers.IO) {
val storageSearch = when(sensorType){
StorageDataBlockType.Humidity_And_Temperature_x100 -> StorageIndexRecordSearcher<StorageActualDataHumidityAndTemperatureX100>()
StorageDataBlockType.Laser_Particle-> StorageIndexRecordSearcher<StorageActualDataLaseParticel>()
StorageDataBlockType.VOC-> StorageIndexRecordSearcher<StorageActualDataVoc>()
StorageDataBlockType.CO2-> StorageIndexRecordSearcher<StorageActualDataCo2>()
StorageDataBlockType.HCHO-> StorageIndexRecordSearcher<StorageActualDataHcho>()
else -> StorageIndexRecordSearcher<StorageActualDataHumidityAndTemperatureX100>()
}
storageSearch.getStorageIndexRecordsNeedSearchFile(
this@HistoryDataTimePickerActivity,
mConnectedViewModel.connectMacAddress,
false,
StorageIndexRecordCondition()
.setStorageDataBlockType(sensorType)
.setInstance(StorageIndexInstance.INSTANCE_DEFAULT)
.setStartTime(when (queryAllData){
true ->0
else ->startTime
})
.setEndTime(when (queryAllData){
true ->systemTime
else ->endTime
})
) { resultList,exception ->
lifecycleScope.launch (Dispatchers.Main){
LoadingDialogUtil.dismissLoadingDialog()
if (exception==null
&&resultList!=null
&&!resultList.isEmpty()
){
LogUtil.e("StorageIndexRecordSearcher ${resultList.size}")
when(sensorType){
StorageDataBlockType.Humidity_And_Temperature_x100 -> {
HtSensorHistoryDataActivity.htResultList = resultList as List<StorageActualDataHumidityAndTemperatureX100>
}
StorageDataBlockType.Laser_Particle-> {
PmSensorHistoryDataActivity.pmResultList = resultList as List<StorageActualDataLaseParticel>
}
StorageDataBlockType.VOC-> {
TvocSensorHistoryDataActivity.vocResultList = resultList as List<StorageActualDataVoc>
}
StorageDataBlockType.CO2-> {
Co2SensorHistoryDataActivity.co2ResultList = resultList as List<StorageActualDataCo2>
}
StorageDataBlockType.HCHO-> {
HchoSensorHistoryDataActivity.hchoResultList = resultList as List<StorageActualDataHcho>
}
else -> {}
}
}else{
}
}
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# 18.清除历史数据
/**
* 清除历史数据
* @param macAddress
* @param force 清除数据 force=true
* @param listener
*/
void clearHistory(@NonNull String macAddress, boolean force, OnModifyConfigurationListener listener);
/**
* 设置清除历史数据
* @return force
*/
suspend fun clearHistory(force:Boolean):Boolean = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext false
}
return@withContext suspendCancellableCoroutine<Boolean> { continuation ->
manager.clearHistory(connectMacAddress!!,force) {
continuation.resume(it, null)
}
}
}
/**
* 清除设备历史数据
*/
private fun cleanHistoryData(){
lifecycleScope.launch(Dispatchers.Main){
LoadingDialogUtil.showLoadingDialog()
val result = mConnectedViewModel.clearHistory( true)
LoadingDialogUtil.dismissLoadingDialog()
ToastUtils.showShort(when(result){
true -> R.string.busi_msg01_clear_successfully
else -> R.string.busi_msg01_clear_failed
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 19.获取设备支持的命令列表
CommandListEntity
| 名称 | 类型 | 描述 |
|---|---|---|
| groupId | int | 对应nanolink协议的groupId |
| commandIds | List | 对应nanolink协议的commandId |
/**
* 获取设备支持的命令列表
* @param macAddress mac地址
* @param listener 监听器
*/
void getDeviceSupportCommandList(@NonNull String macAddress, OnQueryResultListener<List<CommandListEntity>> listener);
/**
* 查询设备支持的命令列表
* @return CommandListEntity?
*/
suspend fun getDeviceSupportCommandList(): List<CommandListEntity>? = withContext(Dispatchers.Default){
if(connectMacAddress == null){
return@withContext null
}
return@withContext suspendCancellableCoroutine<List<CommandListEntity>?> { continuation ->
manager.getDeviceSupportCommandList(connectMacAddress!!,
OnQueryResultListener<List<CommandListEntity>> { _, queryInfo ->
continuation.resume(queryInfo, null)
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 文档更新记录
- 2026/08/20 新增 MSG01设备操作基本功能 API