From 9e76869c36b946eee69fd23d5117313d610ee6d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=AC=A3=E5=8D=97=E7=A7=91=E6=8A=80?= Date: Mon, 24 Feb 2025 18:06:13 +0800 Subject: [PATCH] Iot (#126) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 2025-2-17-iot配置消息处理, 以及控制iot命令发送 (#40) * update:优化 --------- Co-authored-by: Jiao Haoyang <108573524+XuSenfeng@users.noreply.github.com> Co-authored-by: hrz <1710360675@qq.com> --- core/connection.py | 3 + core/handle/audioHandle.py | 2 +- core/handle/iotHandle.py | 156 +++++++++++++++++++++++++++++++++++++ core/handle/textHandle.py | 4 + 4 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 core/handle/iotHandle.py diff --git a/core/connection.py b/core/connection.py index 83251eeb..cbf183b9 100644 --- a/core/connection.py +++ b/core/connection.py @@ -72,6 +72,9 @@ class ConnectionHandler: self.tts_start_speak_time = None self.tts_duration = 0 + # iot相关变量 + self.iot_descriptors = {} + self.cmd_exit = self.config["CMD_exit"] self.max_cmd_length = 0 for cmd in self.cmd_exit: diff --git a/core/handle/audioHandle.py b/core/handle/audioHandle.py index 67615a95..f25c4869 100644 --- a/core/handle/audioHandle.py +++ b/core/handle/audioHandle.py @@ -163,5 +163,5 @@ async def no_voice_close_connect(conn): if no_voice_time > 1000 * close_connection_no_voice_time: conn.client_abort = False conn.asr_server_receive = False - prompt = "时间过得真快,我都好久没说话了。请你用十个字左右话跟我告别,以“再见”或“拜拜拜”为结尾" + prompt = "时间过得真快,我都好久没说话了。请你用十个字左右话跟我告别,以“再见”或“拜拜”为结尾" await startToChat(conn, prompt) \ No newline at end of file diff --git a/core/handle/iotHandle.py b/core/handle/iotHandle.py new file mode 100644 index 00000000..99f059e2 --- /dev/null +++ b/core/handle/iotHandle.py @@ -0,0 +1,156 @@ +import json +from config.logger import setup_logging + +TAG = __name__ +logger = setup_logging() + + +class IotDescriptor: + """ + A class to represent an IoT descriptor. + Attributes: + ---------- + name : str + The name of the IoT descriptor. + description : str + A brief description of the IoT descriptor. + properties : dict + A dictionary containing properties of the IoT descriptor. + methods : dict + A dictionary containing methods of the IoT descriptor. + ------- + """ + + def __init__(self, name, description, properties, methods): + self.name = name + self.description = description + self.properties = [] + self.methods = [] + + # 根据描述创建属性 + for key, value in properties.items(): + # "volume":{"description":"当前音量 值","type":"number"} + """ + 等价于 + { + 'name': 名字, + 'description': 描述, + 'value': 0 + } + """ + # setattr(self, key, {}) # 创建一个空字典, 名字是属性名 + property_item = globals()[key] = {} # 创建一个空字典, 名字是属性名 + property_item['name'] = key + property_item["description"] = value["description"] + if value["type"] == "number": + property_item["value"] = 0 + elif value["type"] == "bool": + property_item["value"] = False + elif value["type"] == "string": + property_item["value"] = "" + else: + raise ValueError("Invalid type") + self.properties.append(property_item) + + # 根据描述创建方法 + for key, value in methods.items(): + # "SetVolume": {"description":"设置音量","parameters":{"volume":{"description":"0到100之间的整数","type":"number"}}} + """ + 等价于 + SetVolume = { + `description`: 描述, + `volume`: { + `description`: 描述, + `value`: 0 + } + } + """ + # setattr(self, key, {}) # 创建一个空字典, 名字是方法名 + method = globals()[key] = {} # 创建一个空字典, 名字是方法名 + method["description"] = value["description"] + method['name'] = key + for k, v in value["parameters"].items(): + # 不同的参数解析 + method[k] = {} + method[k]["description"] = v["description"] + if v["type"] == "number": + method[k]["value"] = 0 + elif v["type"] == "bool": + method[k]["value"] = False + elif v["type"] == "string": + method[k]["value"] = "" + else: + raise ValueError("Invalid type") + + self.methods.append(method) + + +async def handleIotDescriptors(conn, descriptors): + """ + 处理物联网描述 + 示例: [{ + "name":"Speaker", + "description":"当前 AI 机器人的扬声器", + "properties":{ + "volume":{"description":"当前音量 值","type":"number"} 可以有bool, int, string三种类型 + }, + "methods":{ + "SetVolume":{ + "description":"设置音量","parameters":{"volume":{"description":"0到100之间的整数","type":"number"}} + } + } + }] + descriptors: 描述列表 + """ + for descriptor in descriptors: + iot_descriptor = IotDescriptor(descriptor["name"], descriptor["description"], descriptor["properties"], + descriptor["methods"]) + conn.iot_descriptors[descriptor["name"]] = iot_descriptor + + # 暂时从配置文件中设置音量,后期通过意图识别控制音量 + default_iot_volume = 100 + if "iot" in conn.config: + default_iot_volume = conn.config["iot"]["Speaker"]["volume"] + logger.bind(tag=TAG).info(f"服务端设置音量为{default_iot_volume}") + await send_iot_conn(conn, "Speaker", "SetVolume", {"volume": default_iot_volume}) + + +async def send_iot_conn(conn, name, method_name, parameters): + """ + 发送物联网指令 + name: 设备名称 "Speaker" + method: 方法 "SetVolume" + parameters: 参数, 是一个字典 {"volume": 100} + 发送示例: + { + "type": "iot", + "commands": [ + { + "name" : "Speaker", + "method": "SetVolume", + "parameters": { + "volume": 100 + } + } + ] + } + """ + + for key, value in conn.iot_descriptors.items(): + if key == name: + # 找到了设备 + for method in value.methods: + # 找到了方法 + if method["name"] == method_name: + await conn.websocket.send(json.dumps({ + "type": "iot", + "commands": [ + { + "name": name, + "method": method_name, + "parameters": parameters + } + ] + })) + return + logger.bind(tag=TAG).error(f"未找到方法{method_name}") diff --git a/core/handle/textHandle.py b/core/handle/textHandle.py index 9a6faaf0..133fddf7 100644 --- a/core/handle/textHandle.py +++ b/core/handle/textHandle.py @@ -3,6 +3,7 @@ import json from core.handle.abortHandle import handleAbortMessage from core.handle.helloHandle import handleHelloMessage from core.handle.audioHandle import startToChat +from core.handle.iotHandle import handleIotDescriptors TAG = __name__ logger = setup_logging() @@ -36,5 +37,8 @@ async def handleTextMessage(conn, message): conn.asr_audio.clear() if "text" in msg_json: await startToChat(conn, msg_json["text"]) + elif msg_json["type"] == "iot": + if "descriptors" in msg_json: + await handleIotDescriptors(conn, msg_json["descriptors"]) except json.JSONDecodeError: await conn.websocket.send(message)