Commit 86cc6c62 authored by 何金镒's avatar 何金镒

窗帘、空调、新风、地暖的设备控制

parent 1c0c274e
......@@ -5,14 +5,14 @@
## 调光灯(一)
- 包含的设备类型operateId有:503,504
- 包含的设备类型operateId有:
- 支持的命令有:
1、SwitchOpt(开关),对应的参数是true、false
2、BrightnessOpt(调节亮度),对应的参数是0-100
## 调光灯(二)
- 包含的设备类型operateId有:506,507,509,510,511,517,518,701
- 包含的设备类型operateId有:503,504,506,507,509,510,511,517,518,701
- 支持的命令有:
1、SwitchOpt(开关),对应的参数是true、false
2、DimmerAdjustLuminance(调节亮度),对应的参数是0-100
......
......@@ -106,6 +106,12 @@
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<!-- 拼音 -->
<dependency>
<groupId>io.github.biezhi</groupId>
<artifactId>TinyPinyin</artifactId>
<version>2.0.3.RELEASE</version>
</dependency>
</dependencies>
<build>
......
......@@ -52,6 +52,57 @@ public class DeviceTools {
return konkeIotServer.optDeviceClosed(CcuUtils.getCcuName(ccuName), devId, type);
}
@Tool(description = "根据主机号和设备ID控制暂停设备")
public String setPause(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String type){
log.info("mcp调用--->根据主机号和设备ID控制暂停设备....ccuName:{},devId:{},type:{}",ccuName,devId,type);
return konkeIotServer.setPause(CcuUtils.getCcuName(ccuName), devId, type);
}
@Tool(description = "根据主机号和设备ID控制设备移动位置")
public String setMove(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String type,
@ToolParam(description = "移动的距离,范围是0-100")int progress){
log.info("mcp调用--->根据主机号和设备ID控制其移动位置....ccuName:{},devId:{},type:{},progress:{}",ccuName,devId,type,progress);
return konkeIotServer.setMove(CcuUtils.getCcuName(ccuName), devId, type,progress);
}
@Tool(description = "根据主机号和设备ID控制设备延时关机")
public String setDelayedShutdown(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String type,
@ToolParam(description = "多少分钟后关机,范围1-1440")int delayTime){
log.info("mcp调用--->根据主机号和设备ID控制设备延时关机....ccuName:{},devId:{},type:{},delayTime:{}",ccuName,devId,type,delayTime);
return konkeIotServer.setDelayedShutdown(CcuUtils.getCcuName(ccuName), devId, type,delayTime);
}
@Tool(description = "根据主机号和设备ID设置设备的工作温度")
public String setTemperature(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String type,
@ToolParam(description = "设置的温度大小,范围是5-35")double temperature){
log.info("mcp调用--->根据主机号和设备ID设置设备的工作温度....ccuName:{},devId:{},type:{},temperature:{}",ccuName,devId,type,temperature);
return konkeIotServer.setTemperature(CcuUtils.getCcuName(ccuName), devId, type,temperature);
}
@Tool(description = "根据主机号和设备ID设置设备的工作模式")
public String setModel(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String type,
@ToolParam(description = "设置的工作模式,枚举值:WIND、HOT、COLD、DEHUMIDIFICATION、AUTO、MANUAL")String mode){
log.info("mcp调用--->根据主机号和设备ID设置设备的工作模式....ccuName:{},devId:{},type:{},mode:{}",ccuName,devId,type,mode);
return konkeIotServer.setModel(CcuUtils.getCcuName(ccuName), devId, type,mode);
}
@Tool(description = "根据主机号和设备ID设置设备的工作风速")
public String setSpeed(@ToolParam(description = "主机号")String ccuName,
@ToolParam(description = "设备ID")String devId,
@ToolParam(description = "设备类型")String type,
@ToolParam(description = "设置的工作风速,枚举值:LOW、MID、HIGH、AUTO、STOP")String speed){
log.info("mcp调用--->根据主机号和设备ID设置设备的工作风速....ccuName:{},devId:{},type:{},mode:{}",ccuName,devId,type,speed);
return konkeIotServer.setSpeed(CcuUtils.getCcuName(ccuName), devId, type,speed);
}
@Tool(description = "根据主机号和场景ID控制触发场景")
public String triggerScene(@ToolParam(description = "主机号")String ccuName,
......
......@@ -13,6 +13,8 @@ import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Slf4j
......@@ -94,11 +96,18 @@ public class KonkeIotUtils {
case "518":
case "519":
case "701":
case "15005":
case "12505":
action = "SwitchOpt";
break;
case "1001":
case "1003":
case "1014":
action = "MotorOpt";
break;
case "14003":
action = "FreshAirSwitch";
break;
default:
break;
}
......@@ -159,11 +168,18 @@ public class KonkeIotUtils {
case "518":
case "519":
case "701":
case "15005":
case "12505":
action = "SwitchOpt";
break;
case "1001":
case "1003":
case "1014":
action = "MotorOpt";
break;
case "14003":
action = "FreshAirSwitch";
break;
default:
break;
}
......@@ -184,6 +200,264 @@ public class KonkeIotUtils {
return optAIDevice(ccuName, devId, action,opt.toString());
}
/**
* 暂停-目前只有窗帘才支持暂停
*/
public String setPause(String ccuName, String devId,String operateId){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
String action = null;
switch (operateId) {
case "1001":
case "1003":
case "1014":
action = "MotorOpt";
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("MotorOpt".equals(action)){
opt.set("opt", "STOP");
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
/**
* 移动位置-目前只有窗帘电机才支持移动位置
*/
public String setMove(String ccuName, String devId,String operateId,int progress){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
if(progress<0 || progress>100){
throw new StatefulException(-31, "移动的位置只能是0到100");
}
String action = null;
switch (operateId) {
case "1003":
action = "DooyaMotorSeek";
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("DooyaMotorSeek".equals(action)){
opt.set("progress", progress);
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
/**
* 设置延时关机
* delayTime:分钟 最多24小时
*/
public String setDelayedShutdown(String ccuName, String devId,String operateId,int delayTime){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
if(delayTime<0 || delayTime>24*60){
throw new StatefulException(-31, "延时时间在0-24小时之间");
}
String action = null;
switch (operateId) {
case "15005":
action = "FancoilSetDelaytask";
break;
case "12505":
action = "SetFloorheatingOffTime";
break;
case "14003":
action = "SetChopinFreshAirTimeOff";
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("FancoilSetDelaytask".equals(action)){
opt.set("onEnable", false);
opt.set("onDelayTime", "00:00");
opt.set("offEnable", true);
opt.set("offDelayTime", convertMinutesToTime(delayTime));
}else if("SetFloorheatingOffTime".equals(action)){
opt.set("offTime", convertMinutesToTime(delayTime));
}else if("SetChopinFreshAirTimeOff".equals(action)){
opt.set("timeOff", convertMinutesToTime(delayTime));
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
/**
* 将分钟数转换为 HH:mm 格式的时间字符串
*
* @param minutes 分钟数,范围 0-1440
* @return 格式化的时间字符串,如 "00:30", "01:00", "24:00"
* @throws IllegalArgumentException 如果分钟数不在 0-1440 范围内
*/
public static String convertMinutesToTime(int minutes) {
// 验证输入范围
if (minutes < 0 || minutes > 1440) {
throw new IllegalArgumentException("分钟数必须在 0-1440 范围内");
}
// 计算小时和分钟
int hours = minutes / 60;
int mins = minutes % 60;
// 格式化为 HH:mm 字符串
return String.format("%02d:%02d", hours, mins);
}
/**
* 设置温度
*/
public String setTemperature(String ccuName, String devId,String operateId,double temperature){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
if(temperature<5 || temperature>35){
throw new StatefulException(-31, "温度范围在5到35之间");
}
String action = null;
switch (operateId) {
case "15005":
action = "FancoilSetRunTemp";
break;
case "12505":
action = "SetFloorheatingWorkTemperature";
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("FancoilSetRunTemp".equals(action)){
opt.set("runTemp", temperature);
}else if("SetFloorheatingWorkTemperature".equals(action)){
opt.set("temparature", temperature);
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
public final static List<String> mode_15005 = List.of(
"WIND", "HOT", "COLD", "DEHUMIDIFICATION"
);
public final static List<String> mode_12505 = List.of(
"AUTO", "MANUAL"
);
/**
* 设置模式
*/
public String setModel(String ccuName, String devId,String operateId,String mode){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
String action = null;
switch (operateId) {
case "15005"://空调
action = "FancoilSetRunModel";//WIND HOT COLD DEHUMIDIFICATION
if(!mode_15005.contains(mode)){
throw new StatefulException(-32, "不支持的模式");
}
break;
case "12505"://地暖
action = "SetFloorheatingWorkMode";//AUTO MANUAL
if(!mode_12505.contains(mode)){
throw new StatefulException(-32, "不支持的模式");
}
break;
case "14003"://新风
action = "FreshAirSetRunModel";//AUTO MANUAL
if(!mode_12505.contains(mode)){
throw new StatefulException(-32, "不支持的模式");
}
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("FancoilSetRunModel".equals(action)){
opt.set("runModel", mode);
}else if("SetFloorheatingWorkMode".equals(action)){
opt.set("runModel", mode);
}else if("FreshAirSetRunModel".equals(action)){
opt.set("runModel", mode);
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
public final static List<String> speed_15005 = List.of(
"LOW", "MID", "HIGH", "AUTO"
);
public final static List<String> speed_12505 = List.of(
"LOW", "MID", "HIGH", "STOP"
);
/**
* 设置风速
*/
public String setSpeed(String ccuName, String devId,String operateId,String speed){
if (getDeviceOnline(ccuName, devId)) {
throw new StatefulException(-30, "设备已离线");
}
String action = null;
switch (operateId) {
case "15005"://空调
action = "FancoilSetFanSpeed";//LOW MID HIGH AUTO
if(!speed_15005.contains(speed)){
throw new StatefulException(-32, "不支持的风速");
}
break;
case "14003"://新风
action = "FreshAirSetSpeed";//LOW MID HIGH STOP
if(!speed_12505.contains(speed)){
throw new StatefulException(-32, "不支持的风速");
}
break;
default:
break;
}
if(action == null){
throw new StatefulException(-20, "暂不支持该操作");
}
JSONObject opt = new JSONObject();
if("FancoilSetFanSpeed".equals(action)){
opt.set("speed", speed);
}else if("FreshAirSetSpeed".equals(action)){
opt.set("speed", speed);
}else{
throw new StatefulException(-20, "暂不支持该操作");
}
return optAIDevice(ccuName, devId, action,opt.toString());
}
// 如何控制设备
/**
......@@ -218,6 +492,38 @@ public class KonkeIotUtils {
return result2;
}
/**
* 删除场景
*/
public Boolean delScene(String ccuName, String sceneId){
String url = baseUrl + "/2.0/ccu/" + ccuName + "/scene/" + sceneId;
String body = HttpRequest.delete(url)
.header("appId", appId)
.header("appKey", appKey)
.execute().body();
return JSONUtil.parseObj(body).getBool("success");
}
/**
* {
* "name": "场景名称",
* "roomId": 2,
* "actions": [
* {
* "id": -1,
* "realType": "ALL_LIGHT",
* "roomId": 4,
* "delay": 1,
* "operation": "ON",
* "name": "全部灯光"
* }
* ]
* }
*/
public String addScene(String ccuName,Integer roomId,String sceneName){
return "";
}
/**
* {"action":"SwitchOpt","actionArg":{"on":true}}
*/
......
package com.ikonke.konkeaialibabamcp.utils;
import cn.hutool.extra.pinyin.PinyinUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
@Slf4j
public class PinyinUtils {
String[] englishPinYin26={
"EI1", "BI4", "SEI4", "DI4", "YI4", "EFU1", "JI4",
"EIQI1", "AI4", "JEI4", "KEI4", "EOU1", "EMEN1", "EN1",
"OU1", "PI1", "KIU1", "A4", "ESI1", "TI4",
"YOU4", "WEI4", "DABULIU3", "EKESI1", "WAI4", "ZEI4"
};
String englishString26="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
String numberStringArabic="0123456789";
String numberString="零一二三四五六七八九十百点";
String specialHanziString="年级班分";
String myCharAll = numberString + specialHanziString;
List<String> numberPinYin=new ArrayList<String>(20);//数字的拼音(10)
List<String> specialHanziPinYin=new ArrayList<String>(10);//特定汉字集的拼音(除了中文的数字之外的)
List<String> myCharAllPinYin=new ArrayList<String>(40);//所有拼音的集合
boolean fuzzyMatching=true;//是否开启模糊匹配功能
public PinyinUtils(boolean fuzzyMatching){
this.fuzzyMatching = fuzzyMatching;
init();
}
public void init()
{
try{
String str ;
str = numberString;//数字
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
numberPinYin.add(PinyinUtil.getPinyin(c));
}
str = specialHanziString;//汉字
for (int i = 0; i < str.length(); i++) {
char c = str.charAt(i);
specialHanziPinYin.add(PinyinUtil.getPinyin(c));
}
myCharAllPinYin.addAll(numberPinYin);
myCharAllPinYin.addAll(specialHanziPinYin);
} catch (Exception e){
e.printStackTrace();
}
}
public String changeOurWordsWithPinyin(String input){
String output=input;
try{
//处理符号:不关注符合,遇到,就去掉(要保留小数点)
output = changeWordProcessSignal(output);
//处理英文字母:转大写
output = changeWordProcessEnglish(output);
//所有汉字进行相似替换
log.info("input="+input);
int index;
String str;
String strChanged;
StringBuilder strBuilder = new StringBuilder();
for(index=0; index<input.length(); index++){
str = input.substring(index,index+1);
strChanged = changeOneWord(str);
strBuilder.append(strChanged);
}
output=strBuilder.toString();
log.info("output="+output);
} catch (Exception e){
e.printStackTrace();
}
return output;
}
//尾字如果是汉字,进行拼音相同字的替换(零不能替换,可以先转换为0)
public String changeOneWord(String strInput){
//若已经在目标集合中了,就不需要转换了
if(numberString.contains(strInput) || numberStringArabic.contains(strInput)){
log.info("is number");
return strInput;
} else if(specialHanziString.contains(strInput)){
log.info("is specialHanziString");
return strInput;
}
String strChanged;
List<String> listEnglishPinYin = new ArrayList<String>();
strChanged = changeWord(strInput, numberPinYin, numberString);
if(numberString.contains(strChanged)){
log.info("strChanged="+strChanged);
return strChanged;
}
return changeWord(strInput, specialHanziPinYin, specialHanziString);
}
private String changeWord(String strInput, List<String> listPinYin, String strSource) {
//先判断输入,是什么类型的字符:数字、字母、汉字
String strOutput = "";
String str = strInput.substring(0, 1);
String strPinyin = "";
boolean flagGetPinyin = false;
try {
if (str.matches("^[A-Z]{1}$")) {
strPinyin = englishPinYin26[englishString26.indexOf(str)];
log.info("str="+str+" Pinyin="+strPinyin);
flagGetPinyin = true;
} else if (str.matches("^[0-9]{1}$")) {
strPinyin = numberPinYin.get(numberString.indexOf(str));
log.info("str="+str+" Pinyin="+strPinyin);
flagGetPinyin = true;
} else if (str.matches("^[\u4e00-\u9fa5]{1}$")) {
char c = str.charAt(0);
strPinyin = PinyinUtil.getPinyin(c);
flagGetPinyin = true;
}
if(flagGetPinyin) {
//在目标拼音集合中查找匹配项
int num = listPinYin.indexOf(strPinyin);
if (num >= 0) { //拼音精确匹配成功
return strSource.substring(num, num + 1);
} else {
if (fuzzyMatching) {//若开启了模糊匹配
//声母替换
String strPinyinFuzzy = new String(strPinyin);//避免修改原字符串
strPinyinFuzzy = replaceHeadString(strPinyinFuzzy);
boolean flagReplacedHeadString = (strPinyinFuzzy == null) ? false : true;
if (flagReplacedHeadString) {
num = listPinYin.indexOf(strPinyinFuzzy);
if (num >= 0) { //拼音模糊匹配成功
log.info("strPinyinFuzzy=" + strPinyinFuzzy);
return strSource.substring(num, num + 1);
}
}
//韵母替换
strPinyinFuzzy = new String(strPinyin);//避免修改原字符串,不使用声母替换后的字符串
strPinyinFuzzy = replaceTailString(strPinyinFuzzy);
boolean flagReplacedTailString = (strPinyinFuzzy == null) ? false : true;
if (flagReplacedTailString) {
num = listPinYin.indexOf(strPinyinFuzzy);
if (num >= 0) { //拼音模糊匹配成功
log.info("strPinyinFuzzy=" + strPinyinFuzzy);
return strSource.substring(num, num + 1);
}
}
//声母韵母都替换
if (flagReplacedHeadString && flagReplacedTailString) {
strPinyinFuzzy = replaceHeadString(strPinyinFuzzy);
num = listPinYin.indexOf(strPinyinFuzzy);
if (num >= 0) { //拼音模糊匹配成功
log.info("strPinyinFuzzy=" + strPinyinFuzzy);
return strSource.substring(num, num + 1);
}
}
}
}
}
}catch (Exception e){
e.printStackTrace();
}
return flagGetPinyin ? strPinyin : strInput;
}
public String changeWordProcessSignal(String strInput){
String strOutput = strInput;
//去掉 ,。空格-
strOutput = strOutput.replace(",", "");
strOutput = strOutput.replace("。", "");
strOutput = strOutput.replace("-", "");
strOutput = strOutput.replace(" ", "");
return strOutput;
}
public String changeWordProcessEnglish(String strInput){
String strOutput = strInput;
//转大写
strOutput = strOutput.toUpperCase();
return strOutput;
}
private String replaceHeadString(String strPinyin){
//声母替换
String strReplaced = null;
if(strPinyin.contains("ZH")){
strReplaced = strPinyin.replace("ZH", "Z");
} else if(strPinyin.contains("CH")){
strReplaced = strPinyin.replace("CH", "C");
} else if(strPinyin.contains("SH")){
strReplaced = strPinyin.replace("SH", "S");
} else if(strPinyin.contains("Z")){
strReplaced = strPinyin.replace("Z", "ZH");
} else if(strPinyin.contains("C")){
strReplaced = strPinyin.replace("C", "CH");
} else if(strPinyin.contains("S")){
strReplaced = strPinyin.replace("S", "SH");
} else if(strPinyin.contains("L")){
strReplaced = strPinyin.replace("L", "N");
} else if(strPinyin.indexOf('N')==0){ //n有在后面的,n只在做声母时易混
strReplaced = strPinyin.replace("N", "L");
} else {
return null;
}
return strReplaced;//flagReplaced;
}
private String replaceTailString(String strPinyin) {
// 韵母替换
String strReplaced = null;
if (strPinyin.contains("ANG")) {
strReplaced = strPinyin.replace("ANG", "AN");
} else if (strPinyin.contains("ENG")) {
strReplaced = strPinyin.replace("ENG", "EN");
} else if (strPinyin.contains("ING")) {
strReplaced = strPinyin.replace("ING", "IN");
} else if (strPinyin.contains("AN")) {
strReplaced = strPinyin.replace("AN", "ANG");
} else if (strPinyin.contains("EN")) {
strReplaced = strPinyin.replace("EN", "ENG");
} else if (strPinyin.contains("IN")) {
strReplaced = strPinyin.replace("IN", "ING");
} else {
return null;
}
return strReplaced;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment