diff --git a/app/build.gradle b/app/build.gradle index 91889e4..7096e07 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "com.example.iot_controlhost" minSdkVersion 25 targetSdkVersion 30 - versionCode 157 - versionName "3.5.0.157" + versionCode 158 + versionName "3.5.0.158" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { moduleName "mcu" diff --git a/app/src/main/java/android_serialport_api/SerialPortUtil.java b/app/src/main/java/android_serialport_api/SerialPortUtil.java index 8e528a4..e60292f 100644 --- a/app/src/main/java/android_serialport_api/SerialPortUtil.java +++ b/app/src/main/java/android_serialport_api/SerialPortUtil.java @@ -247,6 +247,38 @@ public class SerialPortUtil { return null; } + /** + * 发送无需设备返回的串口指令。 + * 例如部分广播控制命令只写入总线,不会回传响应帧。 + */ + public boolean sendSerialDataNoResponse(Device device, String data) { + boolean isSensor = device instanceof Sensor; + if (!isStart || outputStream == null) { + MyLog.appError("串口未打开但尝试发送数据"); + return false; + } + try { + byte[] sendData = DataUtils.HexToByteArr(data); + outputStream.write(sendData); + outputStream.flush(); + T = "T:" + data; + R = "R:无返回"; + if (isSensor) { + MyLog.sensor(T); + MyLog.sensor(R); + } else { + MyLog.controller(T); + MyLog.controller(R); + } + device.setFailTimes(0); + return true; + } catch (IOException e) { + e.printStackTrace(); + } + device.addFailTimes(); + return false; + } + public boolean isStart() { return isStart; } diff --git a/app/src/main/java/com/example/iot_controlhost/adapter/AutoControllerAdapter.java b/app/src/main/java/com/example/iot_controlhost/adapter/AutoControllerAdapter.java index 7bc4256..d610d8e 100644 --- a/app/src/main/java/com/example/iot_controlhost/adapter/AutoControllerAdapter.java +++ b/app/src/main/java/com/example/iot_controlhost/adapter/AutoControllerAdapter.java @@ -10,9 +10,12 @@ import com.example.iot_controlhost.R; import com.example.iot_controlhost.base.BaseRecyclerAdapter; import com.example.iot_controlhost.databinding.ItemControllerAutoBinding; import com.example.iot_controlhost.model.Result; +import com.example.iot_controlhost.model.controller.Controller; +import com.example.iot_controlhost.model.controller.Dehumidifier485; import com.example.iot_controlhost.model.controller.relay.Relay; import com.example.iot_controlhost.model.sensor.GasSensor; import com.example.iot_controlhost.model.thread.QueueIOUITask; +import com.example.iot_controlhost.ui.dialog.Dehumidifier485Dialog; import com.example.iot_controlhost.utils.DialogUtil; import com.example.iot_controlhost.utils.MyQueue; import com.example.iot_controlhost.utils.global.AutoModelSet; @@ -27,22 +30,18 @@ import java.util.List; import es.dmoral.toasty.Toasty; /** - * 手动控制继电器下的设备 - * 用于主界面显示 - * - * @Author:DuanKaiji + * 自动页面控制设备列表。 */ -public class AutoControllerAdapter extends BaseRecyclerAdapter { +public class AutoControllerAdapter extends BaseRecyclerAdapter { Context mContext; - public AutoControllerAdapter(Context context, List dataList, Class bindingClass) { + public AutoControllerAdapter(Context context, List dataList, Class bindingClass) { super(dataList, bindingClass); mContext = context; } - @Override - protected void bindData(ItemControllerAutoBinding binding, Relay device) { + protected void bindData(ItemControllerAutoBinding binding, Controller device) { TransitionManager.beginDelayedTransition(binding.layoutRoot, new Fade()); if ("未配置".equals(device.getName())) { binding.cardNo.setVisibility(View.VISIBLE); @@ -59,17 +58,15 @@ public class AutoControllerAdapter extends BaseRecyclerAdapter Toasty.info(mContext, "该设备由自动控制系统接管中").show()); } else { binding.cardController.setOnClickListener(view -> { + if (device instanceof Dehumidifier485) { + new Dehumidifier485Dialog(mContext, (Dehumidifier485) device).show(); + return; + } DialogUtil.showLoadingDialog(mContext, mContext.getString(R.string.in_operate)); MyQueue.start(MyQueue.TYPE_CONTROLLER, new QueueIOUITask() { @Override @@ -97,34 +94,85 @@ public class AutoControllerAdapter extends BaseRecyclerAdapter AutoModelSet.getAutoFlorMaxTemp()) { + if (device.getName().equals(RoomSetting.getFloorName()) && RoomSensor.getFloorTemperature() > AutoModelSet.getAutoFlorMaxTemp()) { binding.tvState.setText("高温保护中"); } else { binding.tvState.setText("未启动"); } - binding.ivController.setImageResource(device.getIconClose()); + binding.ivController.setImageResource(getIconClose(device)); binding.cardController.setBackgroundColor(Color.parseColor(RoomSetting.isDarkTheme() ? "#33DCE3E3" : "#F2F0F1")); binding.ivPower.setBackground(mContext.getDrawable(R.drawable.bg_controller_close)); binding.ivPower.setImageDrawable(mContext.getDrawable(R.mipmap.switch_off)); - binding.ivController.setImageDrawable(mContext.getDrawable(device.getIconClose())); + binding.ivController.setImageDrawable(mContext.getDrawable(getIconClose(device))); binding.tvState.setTextColor(Color.parseColor(RoomSetting.isDarkTheme() ? "#FFFFFF" : "#3A3A3A")); binding.tvController.setTextColor(Color.parseColor(RoomSetting.isDarkTheme() ? "#FFFFFF" : "#3A3A3A")); binding.tvPower.setTextColor(Color.parseColor(RoomSetting.isDarkTheme() ? "#FFFFFF" : "#3A3A3A")); } - binding.tvPower.setVisibility(RoomController.relay.getModel().equals(RoomController.relay.getModels()[2]) ? View.VISIBLE : View.GONE); - binding.tvPower.setText("实时功率:" + device.getPower() + "w"); + if (device instanceof Dehumidifier485) { + Dehumidifier485 dehumidifier = (Dehumidifier485) device; + binding.tvPower.setVisibility(View.VISIBLE); + binding.tvPower.setText("环境湿度:" + dehumidifier.getEnvironmentHumidityText()); + } else if (device instanceof Relay && RoomController.relay.getModel().equals(RoomController.relay.getModels()[2])) { + binding.tvPower.setVisibility(View.VISIBLE); + binding.tvPower.setText("实时功率:" + ((Relay) device).getPower() + "w"); + } else { + binding.tvPower.setVisibility(View.GONE); + } + } + + private int getIconOpen(Controller device) { + if (device instanceof Relay) { + return ((Relay) device).getIconOpen(); + } + if (device instanceof Dehumidifier485) { + return R.mipmap.ven_open; + } + return R.mipmap.open; + } + + private int getIconClose(Controller device) { + if (device instanceof Relay) { + return ((Relay) device).getIconClose(); + } + if (device instanceof Dehumidifier485) { + return R.mipmap.ven_close; + } + return R.mipmap.close; + } + + private int getOpenColor(Controller device) { + if (device instanceof Dehumidifier485) { + return Color.parseColor("#00A1FF"); + } + return Color.parseColor("#00BDF4"); + } + + private String getDisplayName(Controller device) { + if (device instanceof Dehumidifier485) { + return ((Dehumidifier485) device).getDisplayName(); + } + return device.getName(); } } diff --git a/app/src/main/java/com/example/iot_controlhost/adapter/ManualControllerAdapter.java b/app/src/main/java/com/example/iot_controlhost/adapter/ManualControllerAdapter.java index f6a2b45..38f2fd0 100644 --- a/app/src/main/java/com/example/iot_controlhost/adapter/ManualControllerAdapter.java +++ b/app/src/main/java/com/example/iot_controlhost/adapter/ManualControllerAdapter.java @@ -8,13 +8,15 @@ import com.example.iot_controlhost.R; import com.example.iot_controlhost.base.BaseRecyclerAdapter; import com.example.iot_controlhost.databinding.ItemControllerBinding; import com.example.iot_controlhost.model.Result; +import com.example.iot_controlhost.model.controller.Controller; +import com.example.iot_controlhost.model.controller.Dehumidifier485; import com.example.iot_controlhost.model.controller.relay.Relay; import com.example.iot_controlhost.model.sensor.GasSensor; import com.example.iot_controlhost.model.thread.QueueIOUITask; +import com.example.iot_controlhost.ui.dialog.Dehumidifier485Dialog; import com.example.iot_controlhost.utils.DialogUtil; import com.example.iot_controlhost.utils.MyQueue; import com.example.iot_controlhost.utils.global.RoomController; -import com.example.iot_controlhost.utils.global.RoomSensor; import com.example.iot_controlhost.utils.global.RoomSetting; import com.example.iot_controlhost.utils.log.UserLog; @@ -23,21 +25,18 @@ import java.util.List; import es.dmoral.toasty.Toasty; /** - * 手动控制继电器下的设备 - * 用于主界面显示 - * - * @Author:DuanKaiji + * 手动控制设备列表。 */ -public class ManualControllerAdapter extends BaseRecyclerAdapter { +public class ManualControllerAdapter extends BaseRecyclerAdapter { Context mContext; - public ManualControllerAdapter(Context context, List dataList, Class bindingClass) { + public ManualControllerAdapter(Context context, List dataList, Class bindingClass) { super(dataList, bindingClass); this.mContext = context; } @Override - protected void bindData(ItemControllerBinding binding, Relay device) { + protected void bindData(ItemControllerBinding binding, Controller device) { if (device.getName().equals(RoomController.fan.getName())) { binding.tvInfo.setText(GasSensor.getQualityAir()); binding.tvInfo.setTextColor(GasSensor.getQualityAirColor()); @@ -45,8 +44,12 @@ public class ManualControllerAdapter extends BaseRecyclerAdapter { + if (device instanceof Dehumidifier485) { + new Dehumidifier485Dialog(mContext, (Dehumidifier485) device).show(); + return; + } DialogUtil.showLoadingDialog(mContext, mContext.getString(R.string.in_operate)); MyQueue.start(MyQueue.TYPE_CONTROLLER, new QueueIOUITask() { @Override @@ -73,7 +76,7 @@ public class ManualControllerAdapter extends BaseRecyclerAdapter 99) { + return false; + } + boolean result = writeRegister(getAddress(), REG_TARGET_HUMIDITY, humidity, true); + if (result) { + setTargetHumidityCache(humidity); + } + return result; + } + + public int getTargetHumidity() { + return MMKVUtil.get(TARGET_HUMIDITY, 60); + } + + public void setTargetHumidityCache(int humidity) { + MMKVUtil.put(TARGET_HUMIDITY, limit(humidity, 5, 99)); + } + + public boolean setTargetTemperature(int temperature) { + if (temperature < 5 || temperature > 70) { + return false; + } + boolean result = writeRegister(getAddress(), REG_TARGET_TEMPERATURE, temperature, true); + if (result) { + setTargetTemperatureCache(temperature); + } + return result; + } + + public int getTargetTemperature() { + return MMKVUtil.get(TARGET_TEMPERATURE, 26); + } + + public void setTargetTemperatureCache(int temperature) { + MMKVUtil.put(TARGET_TEMPERATURE, limit(temperature, 5, 70)); + } + + public boolean setFanSpeed(int fanSpeed) { + if (fanSpeed < FAN_LOW || fanSpeed > FAN_HIGH) { + return false; + } + boolean result = writeRegister(getAddress(), REG_FAN_SPEED, fanSpeed, true); + if (result) { + setFanSpeedCache(fanSpeed); + } + return result; + } + + public int getFanSpeed() { + return MMKVUtil.get(FAN_SPEED, FAN_HIGH); + } + + public void setFanSpeedCache(int fanSpeed) { + MMKVUtil.put(FAN_SPEED, limit(fanSpeed, FAN_LOW, FAN_HIGH)); + } + + public String getFanSpeedName() { + int fanSpeed = getFanSpeed(); + return fanSpeed >= FAN_LOW && fanSpeed <= FAN_HIGH ? FAN_SPEED_NAMES[fanSpeed] : "未知"; + } + + public boolean setWorkMode(int workMode) { + if (workMode < MODE_DEHUMIDIFY || workMode > MODE_VENTILATION) { + return false; + } + boolean result = writeRegister(getAddress(), REG_WORK_MODE, workMode, true); + if (result) { + setWorkModeCache(workMode); + } + return result; + } + + public int getWorkMode() { + return MMKVUtil.get(WORK_MODE, MODE_DEHUMIDIFY); + } + + public void setWorkModeCache(int workMode) { + MMKVUtil.put(WORK_MODE, limit(workMode, MODE_DEHUMIDIFY, MODE_VENTILATION)); + } + + public String getWorkModeName() { + int workMode = getWorkMode(); + return workMode >= MODE_DEHUMIDIFY && workMode <= MODE_VENTILATION ? WORK_MODE_NAMES[workMode] : "未知"; + } + + public boolean readState() { + if (!isAvailable() || "00".equals(getAddress())) { + return false; + } + return readState(getAddress()); + } + + public boolean readState(String address) { + if (StringUtils.isEmpty(address) || "00".equals(address)) { + lastTestState = "00 地址用于广播或读取设备地址,不能读取运行状态"; + return false; + } + String str = address + "0300000014"; + String instruction = str + getCRC(str); + String result = serialPortUtil.sendAndReceiveSerialData(this, instruction); + if (StringUtils.isEmpty(result)) { + lastTestState = "读取状态失败"; + return false; + } + boolean parsed = parseState(result); + lastTestState = parsed ? getDetailState() : "状态数据解析失败"; + return parsed; + } + + public String readCurrentAddress() { + String str = "002500DF0001"; + String instruction = str + getCRC(str); + String result = serialPortUtil.sendAndReceiveSerialData(this, instruction); + if (StringUtils.isEmpty(result) || result.length() < 12) { + return null; + } + try { + return result.substring(6, 8); + } catch (Exception e) { + return null; + } + } + + public String getState() { + if (!isAvailable()) { + return "未正确配置"; + } + if (!isPowerSupply()) { + return "未启动"; + } + String humidityText = environmentHumidity == null ? "" : " 湿度" + environmentHumidity + "%"; + return "运行中 " + getWorkModeName() + humidityText + " 目标" + getTargetHumidity() + "%"; + } + + public String getDetailState() { + return "电源:" + (isPowerSupply() ? "开启" : "关闭") + + "\n目标湿度:" + getTargetHumidity() + "%" + + "\n目标温度:" + getTargetTemperature() + "℃" + + "\n风速:" + getFanSpeedName() + + "\n模式:" + getWorkModeName() + + "\n环境湿度:" + getEnvironmentHumidityText() + + "\n环境温度:" + getEnvironmentTemperatureText() + + "\n工作状态:" + getWorkStatusText() + + "\n故障:" + getFaultText(); + } + + public String getEnvironmentHumidityText() { + return environmentHumidity == null ? "--" : environmentHumidity + "%"; + } + + public String getEnvironmentTemperatureText() { + return environmentTemperature == null ? "--" : environmentTemperature + "℃"; + } + + public String getFaultText() { + if (faultCode == 0) { + return "正常"; + } + StringBuilder builder = new StringBuilder(); + appendFault(builder, 0, "温湿度传感器故障"); + appendFault(builder, 1, "化霜温度1故障"); + appendFault(builder, 2, "化霜温度2故障"); + appendFault(builder, 3, "高压保护"); + appendFault(builder, 4, "低压保护"); + appendFault(builder, 5, "相序保护"); + appendFault(builder, 6, "缺相保护"); + appendFault(builder, 7, "超温保护"); + appendFault(builder, 8, "电源电压超高保护"); + appendFault(builder, 9, "水满告警"); + appendFault(builder, 10, "漏氟保护"); + return builder.length() == 0 ? "未知故障:" + faultCode : builder.toString(); + } + + public String getWorkStatusText() { + if (workStatus == 0) { + return "待机"; + } + StringBuilder builder = new StringBuilder(); + appendStatus(builder, 0, "除湿"); + appendStatus(builder, 1, "化霜"); + appendStatus(builder, 2, "制冷"); + appendStatus(builder, 3, "制热"); + return builder.length() == 0 ? "运行中" : builder.toString(); + } + + @Override + public boolean isAvailable() { + return isEnable() && !StringUtils.isEmpty(getAddress()); + } + + @Override + public String[] getModels() { + return new String[]{"西奈 Z101/Z102/Z103/X86 485除湿机"}; + } + + @Override + public boolean setAddressToNewAddress(String model, String oldAddress, String oldBaud, String newAddress) { + if (!model.equals(getModels()[0])) { + return false; + } + int addressValue = parseHex(newAddress, -1); + if (addressValue < 1 || addressValue > 99) { + return false; + } + if ("00".equals(oldAddress)) { + return writeRegisterNoResponse(oldAddress, REG_SET_ADDRESS, addressValue); + } + return writeRegister(oldAddress, REG_SET_ADDRESS, addressValue, false); + } + + @Override + public boolean setBaudToNewBaud(String model, String oldAddress, String newBaud) { + if (!model.equals(getModels()[0])) { + return false; + } + int baudValue; + switch (newBaud) { + case "4800": + baudValue = 1; + break; + case "9600": + baudValue = 2; + break; + case "19200": + baudValue = 3; + break; + case "115200": + baudValue = 4; + break; + default: + return false; + } + if ("00".equals(oldAddress)) { + return writeRegisterNoResponse(oldAddress, REG_SET_BAUD, baudValue); + } + return writeRegister(oldAddress, REG_SET_BAUD, baudValue, false); + } + + @Override + public boolean test(String deviceModel, String deviceAddress) { + if (!deviceModel.equals(getModels()[0])) { + return false; + } + if ("00".equals(deviceAddress)) { + String address = readCurrentAddress(); + lastTestState = address == null ? "读取设备地址失败" : "当前设备地址:" + address; + return address != null; + } + return readState(deviceAddress); + } + + @Override + public String getTestState() { + return lastTestState; + } + + private boolean parseState(String result) { + try { + if (result.length() < 90 || !"03".equals(result.substring(2, 4))) { + return false; + } + int byteCount = parseHex(result.substring(4, 6), -1); + if (byteCount < 40) { + return false; + } + int power = getRegister(result, 0); + powerSupply = power == 1; + setTargetHumidityCache(getRegister(result, 1)); + setTargetTemperatureCache(getRegister(result, 2)); + setFanSpeedCache(getRegister(result, 3)); + setWorkModeCache(getRegister(result, 4)); + workStatus = getRegister(result, 5); + int humidity = getRegister(result, 6); + environmentHumidity = humidity == 0x00FF ? null : humidity; + int temperature = getRegister(result, 7); + environmentTemperature = temperature == 0x00FF ? null : toSigned16(temperature); + faultCode = getRegister(result, 8); + int coil1 = getRegister(result, 10); + coilTemperature1 = coil1 == 0x00FF ? null : toSigned16(coil1); + int coil2 = getRegister(result, 11); + coilTemperature2 = coil2 == 0x00FF ? null : toSigned16(coil2); + return true; + } catch (Exception e) { + return false; + } + } + + private int getRegister(String result, int index) { + int start = 6 + index * 4; + return parseHex(result.substring(start, start + 4), 0); + } + + private boolean writeRegister(String address, int register, int value, boolean checkAvailable) { + if (checkAvailable && !isAvailable()) { + return false; + } + String str = address + "06" + String.format("%04X", register) + String.format("%04X", value); + String instruction = str + getCRC(str); + return instruction.equals(serialPortUtil.sendAndReceiveSerialData(this, instruction)); + } + + private boolean writeRegisterNoResponse(String address, int register, int value) { + String str = address + "06" + String.format("%04X", register) + String.format("%04X", value); + String instruction = str + getCRC(str); + return serialPortUtil.sendSerialDataNoResponse(this, instruction); + } + + private boolean sendBroadcastPower(String address, boolean state, boolean checkAvailable) { + if (checkAvailable && !isAvailable()) { + return false; + } + String str = address + "06" + String.format("%04X", REG_BROADCAST_POWER) + (state ? "00AA" : "0055"); + String instruction = str + getCRC(str); + return checkAvailable + ? executeInstructionsNoResponse(instruction, state ? "广播开机" : "广播关机") + : serialPortUtil.sendSerialDataNoResponse(this, instruction); + } + + private void appendFault(StringBuilder builder, int bit, String text) { + if ((faultCode & (1 << bit)) == 0) { + return; + } + if (builder.length() > 0) { + builder.append("、"); + } + builder.append(text); + } + + private void appendStatus(StringBuilder builder, int bit, String text) { + if ((workStatus & (1 << bit)) == 0) { + return; + } + if (builder.length() > 0) { + builder.append("、"); + } + builder.append(text); + } + + private int parseHex(String hex, int defaultValue) { + try { + return Integer.parseInt(hex, 16); + } catch (Exception e) { + return defaultValue; + } + } + + private int toSigned16(int value) { + return value > 0x7FFF ? value - 0x10000 : value; + } + + private int limit(int value, int min, int max) { + return Math.max(min, Math.min(max, value)); + } + + private static String[] buildAddressList() { + String[] result = new String[100]; + result[0] = "00"; + for (int i = 1; i <= 99; i++) { + result[i] = String.format("%02X", i); + } + return result; + } +} diff --git a/app/src/main/java/com/example/iot_controlhost/ui/activity/DebugActivity.java b/app/src/main/java/com/example/iot_controlhost/ui/activity/DebugActivity.java index 7013a9c..e231d4c 100644 --- a/app/src/main/java/com/example/iot_controlhost/ui/activity/DebugActivity.java +++ b/app/src/main/java/com/example/iot_controlhost/ui/activity/DebugActivity.java @@ -9,6 +9,7 @@ import com.example.iot_controlhost.R; import com.example.iot_controlhost.base.BaseActivity; import com.example.iot_controlhost.base.SetAddress; import com.example.iot_controlhost.databinding.ActivityDebugBinding; +import com.example.iot_controlhost.model.controller.Dehumidifier485; import com.example.iot_controlhost.model.sensor.Sensor; import com.example.iot_controlhost.model.thread.QueueIOUITask; import com.example.iot_controlhost.ui.dialog.ConfirmDialog; @@ -47,6 +48,7 @@ public class DebugActivity extends BaseActivity deviceNames = new ArrayList<>(); deviceNames.add(RoomController.relay.getName()); deviceNames.add(RoomController.airConditionInfrared.getName()); + deviceNames.add(RoomController.dehumidifier485.getName()); deviceNames.addAll(RoomSensor.getAllSensorNames()); binding.acSensorType.setAdapter(new CustomSpinnerAdapter(mContext, deviceNames.toArray(new String[0]))); binding.acSensorType.addTextChangedListener(new TextWatcher() { @@ -59,6 +61,7 @@ public class DebugActivity extends BaseActivity { switch (integer) { case 2 ->//锁屏 - new PasswordDialog(mContext, "请输入锁屏密码", RoomSetting.getMainActivityLock(), v1 -> { - binding.mainLock.setVisibility(View.GONE); - binding.mainKey.setImageDrawable(getDrawable(R.mipmap.unlock)); - refreshLock(); - }).show(); + showLockPasswordDialog(); case 3 ->//进入设置 new SetBaseDialog(mContext).show(); case 4 -> {//结束登记弹窗 @@ -298,6 +295,19 @@ public class MainActivity extends BaseActivity { + binding.mainLock.setVisibility(View.GONE); + binding.mainKey.setImageDrawable(getDrawable(R.mipmap.unlock)); + refreshLock(); + }); + lockPasswordDialog.setOnDismissListener(dialogInterface -> lockPasswordDialog = null); + lockPasswordDialog.show(); + } + private ScheduledExecutorService mainLockExecutorService; @Override @@ -321,4 +331,4 @@ public class MainActivity extends BaseActivity MyUtil.relaunchApp(); case 2 -> setAirCondition(); case 3 -> setAdvanced(); + case 4 -> setDeHumidity(); } }); presenter.getShowImportAirConditionInstructionDialogLiveData().observe(this, @@ -99,6 +102,8 @@ public class SetActivity extends BaseActivity 除湿机配置"); + setList.clear(); + setList.add(new Set("485除湿机配置", RoomController.dehumidifier485.toString(), view -> setDehumidifier485())); + setList.add(new Set("红外除湿机配置", RoomController.dehumidifierInfrared.toString(), view -> setDeHumidityInfrared())); + setAdapter.setDataList(setList); + } + + void setDehumidifier485() { + toMain = 3; + Dehumidifier485 dehumidifier485 = RoomController.dehumidifier485; + binding.tvTitle.setText(getText(R.string.set) + " > 除湿机配置 > 485除湿机"); + setList.clear(); + setList.add(new Set("启用485除湿机", dehumidifier485.isEnable(), (compoundButton, b) -> { + dehumidifier485.setEnable(b); + setDehumidifier485(); + })); + setList.add(new Set("基础配置", dehumidifier485.toString(), view -> + new SetDeviceBaseDialog(mContext, "485除湿机基础配置", dehumidifier485.getModels(), + getString(R.string.select_model), Dehumidifier485.ADDRESS_LIST, getString(R.string.select_address), (model, address) -> { + dehumidifier485.setModel(model); + dehumidifier485.setAddress(address); + setDehumidifier485(); + }).show())); + setList.add(new Set("控制测试", dehumidifier485.getState(), view -> new Dehumidifier485Dialog(mContext, dehumidifier485).show())); + setAdapter.setDataList(setList); + } + + void setDeHumidityInfrared() { + toMain = 3; DehumidifierInfrared deHumidityInfrared = RoomController.dehumidifierInfrared; binding.tvTitle.setText(getText(R.string.set) + " > 除湿机配置"); setList.clear(); setList.add(new Set(getString(R.string.enable_air_condition_infrared), deHumidityInfrared.isEnable(), (compoundButton, b) -> { deHumidityInfrared.setEnable(b); - setDeHumidity(); + setDeHumidityInfrared(); })); setList.add(new Set(getString(R.string.set_base_infrared), deHumidityInfrared.toString(), view -> new SetDeviceBaseDialog(mContext, getString(R.string.set_base_infrared), deHumidityInfrared.getModels(), getString(R.string.select_model), SpinnerList.ADDRESS, getString(R.string.select_address), (model, address) -> { deHumidityInfrared.setModel(model); deHumidityInfrared.setAddress(address); - setDeHumidity(); + setDeHumidityInfrared(); }).show())); setList.add(new Set("导入除湿机指令", "除湿机指令集", view -> presenter.getAirConditionModelList(-1))); @@ -547,4 +581,4 @@ public class SetActivity extends BaseActivity { + private final Dehumidifier485 dehumidifier; + + public Dehumidifier485Dialog(@NonNull Context mContext, Dehumidifier485 dehumidifier) { + super(mContext); + this.dehumidifier = dehumidifier; + binding.tvTitle.setText(dehumidifier.getName() + "控制"); + if (!SerialPortUtil.getInstance(SerialPortUtil.TYPE_CONTROLLER).openSerialPort()) { + errorTips("串口打开失败"); + } + refreshDehumidifier(); + binding.cardStatus.setOnClickListener(v -> changeDehumidifier(8)); + binding.btnPower.setOnClickListener(v -> changeDehumidifier(0)); + binding.btnHumidityReduce.setOnClickListener(v -> changeDehumidifier(1)); + binding.btnHumidityPlus.setOnClickListener(v -> changeDehumidifier(2)); + binding.modeDehumidify.setOnClickListener(v -> changeDehumidifier(3)); + binding.modeVentilation.setOnClickListener(v -> changeDehumidifier(4)); + binding.fanLow.setOnClickListener(v -> changeDehumidifier(5)); + binding.fanMiddle.setOnClickListener(v -> changeDehumidifier(6)); + binding.fanHigh.setOnClickListener(v -> changeDehumidifier(7)); + } + + private void refreshDehumidifier() { + TransitionManager.beginDelayedTransition(binding.ll, new Fade()); + binding.tvTargetHumidity.setText(dehumidifier.isPowerSupply() ? String.valueOf(dehumidifier.getTargetHumidity()) : "-"); + binding.tvEnvironmentHumidity.setText("环境湿度:" + dehumidifier.getEnvironmentHumidityText()); + binding.tvEnvironmentTemperature.setText("环境温度:" + dehumidifier.getEnvironmentTemperatureText()); + binding.tvWorkStatus.setText("状态:" + dehumidifier.getWorkStatusText()); + binding.tvFault.setText("故障:" + dehumidifier.getFaultText()); + binding.btnPower.setImageDrawable(mContext.getDrawable(dehumidifier.isPowerSupply() ? R.mipmap.open : R.mipmap.close)); + binding.ivSelDehumidify.setVisibility(dehumidifier.getWorkMode() == Dehumidifier485.MODE_DEHUMIDIFY ? View.VISIBLE : View.INVISIBLE); + binding.ivSelVentilation.setVisibility(dehumidifier.getWorkMode() == Dehumidifier485.MODE_VENTILATION ? View.VISIBLE : View.INVISIBLE); + binding.ivSelFanLow.setVisibility(dehumidifier.getFanSpeed() == Dehumidifier485.FAN_LOW ? View.VISIBLE : View.INVISIBLE); + binding.ivSelFanMiddle.setVisibility(dehumidifier.getFanSpeed() == Dehumidifier485.FAN_MIDDLE ? View.VISIBLE : View.INVISIBLE); + binding.ivSelFanHigh.setVisibility(dehumidifier.getFanSpeed() == Dehumidifier485.FAN_HIGH ? View.VISIBLE : View.INVISIBLE); + } + + private void changeDehumidifier(int type) { + DialogUtil.showLoadingDialog(mContext, mContext.getString(R.string.in_operate)); + MyQueue.start(MyQueue.TYPE_CONTROLLER, new QueueIOUITask() { + @Override + public Boolean doInQueueThread() { + switch (type) { + case 0: + if (dehumidifier.isPowerSupply()) { + return dehumidifier.setPowerSupply(false); + } + boolean powerResult = dehumidifier.setPowerSupply(true); + if (powerResult) { + dehumidifier.setWorkMode(dehumidifier.getWorkMode()); + dehumidifier.setFanSpeed(dehumidifier.getFanSpeed()); + dehumidifier.setTargetHumidity(dehumidifier.getTargetHumidity()); + dehumidifier.setTargetTemperature(dehumidifier.getTargetTemperature()); + } + return powerResult; + case 1: + return dehumidifier.setTargetHumidity(dehumidifier.getTargetHumidity() - 1); + case 2: + return dehumidifier.setTargetHumidity(dehumidifier.getTargetHumidity() + 1); + case 3: + return dehumidifier.setWorkMode(Dehumidifier485.MODE_DEHUMIDIFY); + case 4: + return dehumidifier.setWorkMode(Dehumidifier485.MODE_VENTILATION); + case 5: + return dehumidifier.setFanSpeed(Dehumidifier485.FAN_LOW); + case 6: + return dehumidifier.setFanSpeed(Dehumidifier485.FAN_MIDDLE); + case 7: + return dehumidifier.setFanSpeed(Dehumidifier485.FAN_HIGH); + case 8: + return dehumidifier.readState(); + default: + return false; + } + } + + @Override + public void doInUIThread(Boolean result) { + if (result) { + successTips(dehumidifier.getState()); + UserLog.operate("手动:操作485除湿机 " + dehumidifier.getState()); + } else { + errorTips("操作485除湿机失败"); + UserLog.operateError("手动:操作485除湿机失败"); + } + refreshDehumidifier(); + DialogUtil.hideLoadingDialog(); + } + }); + } + + @Override + public void dismiss() { + super.dismiss(); + if (RoomSetting.getMode() == 0) { + RxBusUtils.get().post(RxTag.UPDATE_MANUAL, 0); + } else if (RoomSetting.getMode() == 1) { + RxBusUtils.get().post(RxTag.UPDATE_AUTO, 2); + } + } +} diff --git a/app/src/main/java/com/example/iot_controlhost/ui/fragment/AutoFragment.java b/app/src/main/java/com/example/iot_controlhost/ui/fragment/AutoFragment.java index d9274f3..cccc541 100644 --- a/app/src/main/java/com/example/iot_controlhost/ui/fragment/AutoFragment.java +++ b/app/src/main/java/com/example/iot_controlhost/ui/fragment/AutoFragment.java @@ -14,6 +14,7 @@ import com.example.iot_controlhost.base.BaseMode; import com.example.iot_controlhost.databinding.FragmentModelAutoBinding; import com.example.iot_controlhost.databinding.ItemControllerAutoBinding; import com.example.iot_controlhost.model.controller.AirConditionInfrared; +import com.example.iot_controlhost.model.controller.Controller; import com.example.iot_controlhost.model.controller.Floor; import com.example.iot_controlhost.model.controller.relay.Relay; import com.example.iot_controlhost.ui.dialog.AirDialog; @@ -68,7 +69,7 @@ public class AutoFragment extends BaseFragment devices = RoomController.getAllControlRelays(); + List devices = RoomController.getAllControlDevices(); // 第一排显示2个设备 if (devices.size() <= 2) { int missingCount = 2 - devices.size(); @@ -78,9 +79,9 @@ public class AutoFragment extends BaseFragment remainingDevices = new ArrayList<>(); + List remainingDevices = new ArrayList<>(); if (devices.size() > 2) { - List tempDevices = devices.subList(2, devices.size()); + List tempDevices = devices.subList(2, devices.size()); remainingDevices.addAll(tempDevices); } if (remainingDevices.size() < 4) { @@ -150,6 +151,7 @@ public class AutoFragment extends BaseFragment devices = RoomController.getAllControlRelays(); + List devices = RoomController.getAllControlDevices(); int size = devices.size(); if (size < 6) { for (int i = 0; i < 6 - size; i++) { @@ -151,4 +152,4 @@ public class ManualFragment extends BaseFragment max) { + if (cur > max || cur > target && needDehumidifyToTarget) { + needDehumidifyToTarget = true; needDehumidify = true; + } else { + needDehumidifyToTarget = false; + needDehumidify = false; } // === 互斥处理 === if (needHumidify && needDehumidify) { // 理论不会发生,保险处理 needDehumidify = false; + needDehumidifyToTarget = false; } boolean humidifierState = needHumidify; boolean dehumidifierState = needDehumidify; + Controller autoDehumidifier = RoomController.getAutoDehumidifier(); + int dehumidifierTargetHumidity = (int) Math.round(target); + boolean needRefreshDehumidifierSetting = autoDehumidifier instanceof Dehumidifier485 + && dehumidifierState + && ((Dehumidifier485) autoDehumidifier).getTargetHumidity() != dehumidifierTargetHumidity; + boolean humidifierKeep = !RoomController.humidifier.isAvailable() || RoomController.humidifier.isPowerSupply() == humidifierState; + boolean dehumidifierKeep = autoDehumidifier == null + || autoDehumidifier.isPowerSupply() == dehumidifierState && !needRefreshDehumidifierSetting; - if (RoomController.humidifier.isPowerSupply() == humidifierState && RoomController.dehumidifier.isPowerSupply() == dehumidifierState) { + if (humidifierKeep && dehumidifierKeep) { MyLog.auto("湿度-控制维持当前状态"); } else { // 加湿器控制 @@ -102,10 +118,15 @@ public class HumidityController extends Control { })); } // 除湿器控制(新增) - if (RoomController.dehumidifier.isAvailable()) { + if (autoDehumidifier != null) { MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> { MyLog.auto("自动:" + (dehumidifierState ? "开启" : "关闭") + "除湿机--"); - MyUtil.autoControlOperateThird(RoomController.dehumidifier, dehumidifierState); + if (autoDehumidifier instanceof Dehumidifier485 && dehumidifierState) { + Dehumidifier485 dehumidifier485 = (Dehumidifier485) autoDehumidifier; + dehumidifier485.setWorkMode(Dehumidifier485.MODE_DEHUMIDIFY); + dehumidifier485.setTargetHumidity(dehumidifierTargetHumidity); + } + MyUtil.autoControlOperateThird(autoDehumidifier, dehumidifierState); })); } } @@ -115,8 +136,14 @@ public class HumidityController extends Control { @Override protected void stopMethod() { + needOpenToTarget = false; + needDehumidifyToTarget = false; MyQueue.getInstance(MyQueue.TYPE_CONTROLLER).addTask(new QueueIOTask(() -> { MyUtil.autoControlOperateThird(RoomController.humidifier); + Controller autoDehumidifier = RoomController.getAutoDehumidifier(); + if (autoDehumidifier != null) { + MyUtil.autoControlOperateThird(autoDehumidifier, false); + } String airExchangeMode = AutoModelSet.getAirExchangeMode(); if (RoomController.airExchange.isAvailable() && airExchangeMode.equals(SpinnerList.AIR_EXCHANGE_MODES[2])) { MyUtil.autoControlOperateThird(RoomController.airExchange, false); diff --git a/app/src/main/java/com/example/iot_controlhost/utils/global/RoomController.java b/app/src/main/java/com/example/iot_controlhost/utils/global/RoomController.java index 564bbef..93ae5a8 100644 --- a/app/src/main/java/com/example/iot_controlhost/utils/global/RoomController.java +++ b/app/src/main/java/com/example/iot_controlhost/utils/global/RoomController.java @@ -3,6 +3,8 @@ package com.example.iot_controlhost.utils.global; import com.example.iot_controlhost.R; import com.example.iot_controlhost.model.Device; import com.example.iot_controlhost.model.controller.AirConditionInfrared; +import com.example.iot_controlhost.model.controller.Controller; +import com.example.iot_controlhost.model.controller.Dehumidifier485; import com.example.iot_controlhost.model.controller.DehumidifierInfrared; import com.example.iot_controlhost.model.controller.Floor; import com.example.iot_controlhost.model.controller.relay.AirCondition; @@ -59,6 +61,10 @@ public class RoomController { * 除湿机 */ public static DehumidifierInfrared dehumidifierInfrared = new DehumidifierInfrared(DEVICE_NAME[15]); + /** + * 485除湿机 + */ + public static Dehumidifier485 dehumidifier485 = new Dehumidifier485("485除湿机"); //——————————————————————————————————————————————以下为继电器下设备—————————————————————————————————————————————————— /** @@ -132,6 +138,7 @@ public class RoomController { devices.add(floor); devices.add(airConditionInfrared); devices.add(dehumidifierInfrared); + devices.add(dehumidifier485); return devices; } @@ -174,6 +181,31 @@ public class RoomController { * @param deviceName 设备名称 需要在 PublicClass.DEVICE_NAME选用 * @return 设备类 可用于设置端口地址等操作 */ + /** + * 获得手动/自动页面可直接控制的设备。 + */ + public static List getAllControlDevices() { + List devices = new ArrayList<>(); + devices.addAll(getAllControlRelays()); + if (dehumidifier485.isAvailable()) { + devices.add(dehumidifier485); + } + return devices; + } + + /** + * 湿度自动控制优先使用485除湿机,未配置时兼容旧继电器除湿机。 + */ + public static Controller getAutoDehumidifier() { + if (dehumidifier485.isAvailable()) { + return dehumidifier485; + } + if (dehumidifier.isAvailable()) { + return dehumidifier; + } + return null; + } + public static Relay getRelay(String deviceName) { for (int i = 0; i < DEVICE_NAME.length; i++) { if (DEVICE_NAME[i].equals(deviceName)) { @@ -225,6 +257,7 @@ public class RoomController { //关闭单独设备 MyInfraredUtils.getInstances().setPowerSupply(false); RoomController.floor.setPowerSupply(false); + RoomController.dehumidifier485.setPowerSupply(false); //关闭继电器下的所有设备 List devices = getAllAvailableRelays(); //除了空调物理电源 @@ -309,6 +342,7 @@ public class RoomController { List relays = RoomController.getAllAvailableRelays(); MyInfraredUtils.getInstances().setPowerSupply(false); RoomController.dehumidifier.setPowerSupply(false); + RoomController.dehumidifier485.setPowerSupply(false); RoomController.floor.setPowerSupply(false); relays.remove(RoomController.airCondition); relays.remove(RoomController.dehumidifier); @@ -316,4 +350,4 @@ public class RoomController { r.setPowerSupply(false); } } -} \ No newline at end of file +} diff --git a/app/src/main/res/layout/dialog_dehumidifier_485.xml b/app/src/main/res/layout/dialog_dehumidifier_485.xml new file mode 100644 index 0000000..34435c3 --- /dev/null +++ b/app/src/main/res/layout/dialog_dehumidifier_485.xml @@ -0,0 +1,463 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +