diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml new file mode 100644 index 0000000..5e05c18 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml new file mode 100644 index 0000000..5e05c18 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml new file mode 100644 index 0000000..bf62be5 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml new file mode 100644 index 0000000..5e05c18 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml new file mode 100644 index 0000000..bf62be5 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml new file mode 100644 index 0000000..f092228 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml new file mode 100644 index 0000000..5e05c18 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml new file mode 100644 index 0000000..bf62be5 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml new file mode 100644 index 0000000..f092228 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_gateway.xml b/app/src/main/res/layout/activity_gateway.xml new file mode 100644 index 0000000..cc7cacc --- /dev/null +++ b/app/src/main/res/layout/activity_gateway.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml new file mode 100644 index 0000000..5e05c18 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml new file mode 100644 index 0000000..bf62be5 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml new file mode 100644 index 0000000..f092228 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_gateway.xml b/app/src/main/res/layout/activity_gateway.xml new file mode 100644 index 0000000..cc7cacc --- /dev/null +++ b/app/src/main/res/layout/activity_gateway.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scan_zigbee.xml b/app/src/main/res/layout/activity_scan_zigbee.xml new file mode 100644 index 0000000..15cdf02 --- /dev/null +++ b/app/src/main/res/layout/activity_scan_zigbee.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1d57b75..03d3f5d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -73,6 +73,8 @@ + + "智能壁挂炉" "pc" -> "Wi-Fi智能插排" "cz" -> "Wi-Fi智能插座" - "wg2" -> "多功能网关" + "wg2" -> "智能网关" else -> "其他" } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt index d1f82d6..7f4b807 100644 --- a/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt +++ b/app/src/main/java/com/casic/br/fragment/DevicePageFragment.kt @@ -158,6 +158,7 @@ } selectedCategory.contains("wg2") -> { //网关 + requireContext().navigatePageTo(deviceModel.devId) } } } else { diff --git a/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt new file mode 100644 index 0000000..f435159 --- /dev/null +++ b/app/src/main/java/com/casic/br/utils/TuyaSubDeviceListener.kt @@ -0,0 +1,46 @@ +package com.casic.br.utils + +import com.tuya.smart.sdk.api.ISubDevListener + +open class TuyaSubDeviceListener : ISubDevListener { + + /** + * 当设备功能状态变更时的通知 + * + * @param nodeId 子设备 nodeId,子设备 DeviceBean 中的 nodeId 字段 + * @param dpStr 子设备变更的设备功能数据 + */ + override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { + + } + + /** + * 设备移除时的通知 + */ + override fun onSubDevRemoved(devId: String?) { + + } + + /** + * 新增设备时的通知 + */ + override fun onSubDevAdded(devId: String?) { + + } + + /** + * 子设备重命名时的通知 + */ + override fun onSubDevInfoUpdate(devId: String?) { + + } + + /** + * 子设备在线 or 离线状态变更通知 + */ + override fun onSubDevStatusChanged( + onlineDeviceIds: MutableList?, offlineDeviceIds: MutableList? + ) { + + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt new file mode 100644 index 0000000..d3ee472 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/ScanZigbeeActivity.kt @@ -0,0 +1,182 @@ +package com.casic.br.view + +import android.os.CountDownTimer +import android.os.Handler +import android.util.Log +import android.view.KeyEvent +import android.view.View +import androidx.lifecycle.ViewModelProvider +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.utils.DeserializeModel +import com.casic.br.utils.LoadingDialogHub +import com.casic.br.vm.DeviceViewModel +import com.casic.br.widgets.WaterRippleView +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.vm.LoadState +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.home.sdk.builder.TuyaGwSubDevActivatorBuilder +import com.tuya.smart.sdk.api.ITuyaActivator +import com.tuya.smart.sdk.api.ITuyaSmartActivatorListener +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.* +import kotlinx.android.synthetic.main.activity_scan_zigbee.deviceLogoView +import kotlinx.android.synthetic.main.activity_scan_zigbee.rootView +import kotlinx.android.synthetic.main.include_base_title.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors + +class ScanZigbeeActivity : KotlinBaseActivity() { + + private val kTag = "ScanZigbeeActivity" + private var isRunning = true + private lateinit var singleThreadExecutor: ExecutorService + private lateinit var deviceId: String + private lateinit var deviceViewModel: DeviceViewModel + private lateinit var subDevActivator: ITuyaActivator + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceBean: DeviceBean + + override fun initLayoutView(): Int = R.layout.activity_scan_zigbee + + override fun setupTopBarLayout() { + ImmersionBar.with(this).statusBarDarkFont(true).init() + initLayoutImmersionBar(rootView) + leftBackView.setOnClickListener { finish() } + titleView.text = String.format("搜索Zigbee设备") + } + + override fun observeRequestState() { + deviceViewModel.loadState.observe(this, { + when (it) { + LoadState.Success -> { + LoadingDialogHub.dismiss() + navigatePageTo() + } + else -> { + "添加失败,请重新绑定".show(this) + LoadingDialogHub.dismiss() + } + } + }) + } + + override fun initData() { + //只有一个核心线程,当被占用时,其他的任务需要进入队列等待 + singleThreadExecutor = Executors.newSingleThreadExecutor() + waterRippleView.setOnAnimationStartListener(object : + WaterRippleView.OnAnimationStartListener { + override fun onStart(view: WaterRippleView?) { + view?.start() + //开启线程搜索设备 + Log.d(kTag, "onStart: 开始线程") + singleThreadExecutor.execute(searchRunnable) + isRunning = true + } + }) + + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + deviceViewModel = ViewModelProvider(this)[DeviceViewModel::class.java] + weakReferenceHandler = WeakReferenceHandler(callback) + + val configBuilder = TuyaGwSubDevActivatorBuilder().setDevId(deviceId) + .setListener(object : ITuyaSmartActivatorListener { + override fun onError(errorCode: String?, errorMsg: String?) { + Log.e(kTag, "onError: $errorMsg") + } + + override fun onActiveSuccess(devResp: DeviceBean?) { + if (devResp == null) { + Log.e(kTag, "onActiveSuccess: 无法扫描到涂鸦Zigbee模组") + return + } + deviceBean = devResp + tipsView.text = String.format("已搜索到Zigbee设备,基本信息如下") + logoViewLayout.visibility = View.VISIBLE + Glide.with(this@ScanZigbeeActivity) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) + deviceNameView.text = String.format("产品名称:${deviceBean.name}") + + //将绑定的设备存入自己的数据库 + weakReferenceHandler.sendEmptyMessage(2022101902) + } + + override fun onStep(step: String?, data: Any?) { + + } + }) + + subDevActivator = TuyaHomeSdk.getActivatorInstance().newGwSubDevActivator(configBuilder) + subDevActivator.start() + } + + private val callback = Handler.Callback { + if (it.what == 2022101902) { + LoadingDialogHub.show(this, "设备绑定成功,开始同步服务器数据") + object : CountDownTimer(5000, 1000) { + override fun onTick(millisUntilFinished: Long) { + + } + + override fun onFinish() { + deviceViewModel.addDevice(deviceBean, DeserializeModel.getUserModel()!!) + } + }.start() + } + true + } + + override fun initEvent() { + + } + + private val searchRunnable = Runnable { + while (true) { + try { + if (!isRunning) { + Log.d(kTag, "run: 设备搜索线程休眠中...") + Thread.sleep(Long.MAX_VALUE) + } + } catch (e: Exception) { + e.printStackTrace() + } + Log.d(kTag, "run: 设备搜索线程运行中...") + try { + //搜索设备 + Thread.sleep(1000) + } catch (e: InterruptedException) { + e.printStackTrace() + } + } + } + + override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { + if (keyCode == KeyEvent.KEYCODE_BACK) { + subDevActivator.stop() + } + return super.onKeyDown(keyCode, event) + } + + override fun onPause() { + super.onPause() + isRunning = false + waterRippleView.stop() + subDevActivator.stop() + } + + override fun onDestroy() { + super.onDestroy() + subDevActivator.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt index d853d48..4d22582 100644 --- a/app/src/main/java/com/casic/br/view/device/CookerActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/CookerActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.cooker.ControlPageFragment import com.casic.br.fragment.cooker.ServicePageFragment import com.casic.br.fragment.cooker.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt index d0267c1..435a2fe 100644 --- a/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/FireplaceActivity.kt @@ -10,6 +10,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.fireplace.ControlPageFragment import com.casic.br.fragment.fireplace.ServicePageFragment import com.casic.br.fragment.fireplace.StatusPageFragment @@ -69,7 +70,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐壁挂炉" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt index 344ac9a..68a0785 100644 --- a/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/PowerStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -21,10 +22,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_cooker.* import kotlinx.android.synthetic.main.activity_power_strip.* -import kotlinx.android.synthetic.main.activity_power_strip.rootView -import kotlinx.android.synthetic.main.fragment_device_cooker_control.* import kotlinx.android.synthetic.main.include_device_title.* /** @@ -63,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() deviceBean.dpName.entries.forEach { when (it.key) { "1" -> firstNameView.text = it.value diff --git a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt index 90292cc..c02c881 100644 --- a/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/RangeHoodActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.rangehood.ControlPageFragment import com.casic.br.fragment.rangehood.ServicePageFragment import com.casic.br.fragment.rangehood.StatusPageFragment @@ -73,7 +74,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐油烟机" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt index 122ef71..4ea83fc 100644 --- a/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/SingleStripActivity.kt @@ -6,6 +6,7 @@ import com.casic.br.R import com.casic.br.extensions.createCommand import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.utils.CommandManager import com.casic.br.utils.TuyaDeviceListener import com.gyf.immersionbar.ImmersionBar @@ -60,6 +61,7 @@ return } Log.d(kTag, deviceBean.dps.toJson()) + titleView.text = deviceBean.toChineseTypeName() changeViewStatus(deviceBean.dps["1"] as Boolean) deviceInstance.registerDevListener(object : TuyaDeviceListener() { diff --git a/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt new file mode 100644 index 0000000..5ab7f58 --- /dev/null +++ b/app/src/main/java/com/casic/br/view/device/SmartGatewayActivity.kt @@ -0,0 +1,285 @@ +package com.casic.br.view.device + +import android.graphics.Color +import android.os.Handler +import android.util.Log +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.casic.br.R +import com.casic.br.adapter.AddedDeviceAdapter +import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.selectCategory +import com.casic.br.extensions.toChineseTypeName +import com.casic.br.view.ScanZigbeeActivity +import com.gyf.immersionbar.ImmersionBar +import com.pengxh.kt.lite.base.KotlinBaseActivity +import com.pengxh.kt.lite.extensions.navigatePageTo +import com.pengxh.kt.lite.extensions.show +import com.pengxh.kt.lite.utils.Constant +import com.pengxh.kt.lite.utils.WeakReferenceHandler +import com.pengxh.kt.lite.widget.dialog.AlertControlDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet +import com.tuya.smart.android.device.bean.UpgradeInfoBean +import com.tuya.smart.home.sdk.TuyaHomeSdk +import com.tuya.smart.sdk.api.* +import com.tuya.smart.sdk.bean.DeviceBean +import kotlinx.android.synthetic.main.activity_gateway.* +import kotlinx.android.synthetic.main.include_device_title.* +import java.util.* + +/** + * 智能网关 + * TODO 数据上报暂缓 + * */ +class SmartGatewayActivity : KotlinBaseActivity() { + + private val kTag = "SmartGatewayActivity" + private val context = this@SmartGatewayActivity + private var dataBeans: MutableList = ArrayList() + private var isRefresh = false + private lateinit var deviceId: String + private lateinit var weakReferenceHandler: WeakReferenceHandler + private lateinit var deviceAdapter: AddedDeviceAdapter + private lateinit var tuyaOTA: ITuyaOta + private lateinit var deviceInstance: ITuyaDevice //网关本身 + private lateinit var deviceSubInstance: ITuyaGateway //网关下属设备 + + override fun initLayoutView(): Int = R.layout.activity_gateway + + override fun observeRequestState() { + + } + + override fun setupTopBarLayout() { + initLayoutImmersionBar(rootView) + ImmersionBar.with(this).statusBarDarkFont(true).init() + + leftBackView.setOnClickListener { finish() } + } + + override fun initData() { + deviceId = intent.getStringExtra(Constant.INTENT_PARAM)!! + tuyaOTA = TuyaHomeSdk.newOTAInstance(deviceId) + //控制设备前必须初始家庭化数据,每次 App 活跃期间,初始化一次即可。 + deviceInstance = TuyaHomeSdk.newDeviceInstance(deviceId) + deviceSubInstance = TuyaHomeSdk.newGatewayInstance(deviceId) + + weakReferenceHandler = WeakReferenceHandler(callback) + + val deviceBean = TuyaHomeSdk.getDataInstance().getDeviceBean(deviceId) + if (deviceBean == null) { + "查询设备信息失败,请检查设备是否已离线".show(this) + return + } + deviceStateView.text = if (deviceBean.isOnline) { + "设备在线" + } else { + "设备已离线" + } + titleView.text = deviceBean.toChineseTypeName() + Glide.with(this) + .load(deviceBean.iconUrl) + .apply(RequestOptions.circleCropTransform()) + .placeholder(R.mipmap.load_image_error) + .into(deviceLogoView) +// deviceSubInstance.registerSubDevListener(object :TuyaSubDeviceListener(){ +// override fun onSubDevDpUpdate(nodeId: String?, dps: String?) { +// Log.d(kTag, "onDpUpdate: $dps") +// } +// }) + } + + override fun onResume() { + super.onResume() + deviceSubInstance.getSubDevList(object : ITuyaDataCallback> { + override fun onSuccess(result: MutableList) { + when { + isRefresh -> { + dataBeans.clear() + dataBeans = result + isRefresh = false + } + else -> { + dataBeans = result + } + } + weakReferenceHandler.sendEmptyMessage(2022101901) + } + + override fun onError(errorCode: String?, errorMessage: String?) { + Log.d(kTag, "onError: $errorMessage") + } + }) + } + + private val callback = Handler.Callback { + if (it.what == 2022101901) { + if (isRefresh) { + deviceAdapter.notifyDataSetChanged() + } else { + deviceAdapter = AddedDeviceAdapter(this, dataBeans) + deviceRecyclerView.adapter = deviceAdapter + deviceAdapter.setOnItemClickListener(object : + AddedDeviceAdapter.OnItemClickListener { + override fun onAddDeviceClick() { + navigatePageTo(deviceId) + } + + override fun onItemClick(position: Int) { + // 需要根据设备类型显示不同的页面 + val deviceModel = dataBeans[position] + val selectedCategory = deviceModel.selectCategory() + Log.d(kTag, "设备类型: $selectedCategory") + when { +// selectedCategory.contains("rs") -> { +// //热水器 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("yyj") -> { +// //油烟机 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("bgl") -> { +// //壁挂炉 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("rqz") -> { +// //燃气灶 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("pc") -> { +// //排插 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("cz") -> { +// //插座 +// navigatePageTo(deviceModel.devId) +// } +// selectedCategory.contains("wg2") -> { +// //网关 +// navigatePageTo(deviceModel.devId) +// } + } + } + + override fun onButtonClick(position: Int) { + moreOperate(position) + } + }) + } + } + true + } + + private fun moreOperate(clickPosition: Int) { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("删除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : + BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> TuyaHomeSdk.newDeviceInstance(dataBeans[clickPosition].devId) + .removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + dataBeans.removeAt(clickPosition) + deviceAdapter.notifyItemRemoved(clickPosition) + deviceAdapter.notifyItemRangeChanged( + clickPosition, dataBeans.size - clickPosition + ) + } + }) + } + } + }).build().show() + } + + override fun initEvent() { + rightOperateView.setOnClickListener { + BottomActionSheet.Builder() + .setContext(this) + .setActionItemTitle(arrayListOf("固件升级", "移除设备")) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + when (position) { + 0 -> { + tuyaOTA.getOtaInfo(object : IGetOtaInfoCallback { + override fun onSuccess(upgradeInfoBeans: MutableList?) { + if (upgradeInfoBeans.isNullOrEmpty()) { + "无新版本可用".show(context) + } + val mainModel = upgradeInfoBeans!![0] + when (mainModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "主联网模组无新版本可用".show(context) + } + + val mcuModel = upgradeInfoBeans[1] + when (mcuModel.upgradeStatus) { + 1 -> updateVersion() + 2 -> { + + } + else -> "MCU模块无新版本可用".show(context) + } + } + + override fun onFailure(code: String?, error: String?) { + + } + }) + } + 1 -> deviceInstance.removeDevice(object : IResultCallback { + override fun onError(code: String?, error: String?) { + Log.d(kTag, "onError: $error") + } + + override fun onSuccess() { + finish() + } + }) + } + } + }) + .build().show() + } + } + + private fun updateVersion() { + AlertControlDialog.Builder() + .setContext(this) + .setTitle("版本升级") + .setMessage("有新版本可以升级,是否升级?") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : + AlertControlDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + tuyaOTA.startOta() + } + + override fun onConfirmClick() { + + } + }) + .build().show() + } + + override fun onDestroy() { + super.onDestroy() + deviceInstance.unRegisterDevListener() + deviceInstance.onDestroy() + + deviceSubInstance.unRegisterSubDevListener() + deviceSubInstance.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt index 007593e..0aa82f6 100644 --- a/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt +++ b/app/src/main/java/com/casic/br/view/device/WaterHeaterActivity.kt @@ -11,6 +11,7 @@ import com.casic.br.R import com.casic.br.adapter.TabPagerAdapter import com.casic.br.extensions.initLayoutImmersionBar +import com.casic.br.extensions.toChineseTypeName import com.casic.br.fragment.waterheater.ControlPageFragment import com.casic.br.fragment.waterheater.ServicePageFragment import com.casic.br.fragment.waterheater.StatusPageFragment @@ -30,12 +31,7 @@ import com.tuya.smart.sdk.api.IResultCallback import com.tuya.smart.sdk.api.ITuyaDevice import com.tuya.smart.sdk.api.ITuyaOta -import kotlinx.android.synthetic.main.activity_range_hood.* import kotlinx.android.synthetic.main.activity_water_heater.* -import kotlinx.android.synthetic.main.activity_water_heater.deviceLogoView -import kotlinx.android.synthetic.main.activity_water_heater.deviceTabLayout -import kotlinx.android.synthetic.main.activity_water_heater.deviceViewPager -import kotlinx.android.synthetic.main.activity_water_heater.rootView import kotlinx.android.synthetic.main.include_device_title.* import java.util.* @@ -73,7 +69,7 @@ "查询设备信息失败,请检查设备是否已离线".show(this) return } - titleView.text = "万家乐热水器" + titleView.text = deviceBean.toChineseTypeName() Glide.with(this) .load(deviceBean.iconUrl) .placeholder(R.mipmap.load_image_error) diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml deleted file mode 100644 index bf62be5..0000000 --- a/app/src/main/res/drawable/bg_stroke_layout_gray_circle.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml new file mode 100644 index 0000000..5e05c18 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml new file mode 100644 index 0000000..bf62be5 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_gray_circle_7.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml new file mode 100644 index 0000000..f092228 --- /dev/null +++ b/app/src/main/res/drawable/bg_stroke_layout_main_circle_1.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_gateway.xml b/app/src/main/res/layout/activity_gateway.xml new file mode 100644 index 0000000..cc7cacc --- /dev/null +++ b/app/src/main/res/layout/activity_gateway.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scan_zigbee.xml b/app/src/main/res/layout/activity_scan_zigbee.xml new file mode 100644 index 0000000..15cdf02 --- /dev/null +++ b/app/src/main/res/layout/activity_scan_zigbee.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_single_strip.xml b/app/src/main/res/layout/activity_single_strip.xml index 8790073..8fc949d 100644 --- a/app/src/main/res/layout/activity_single_strip.xml +++ b/app/src/main/res/layout/activity_single_strip.xml @@ -17,7 +17,7 @@