mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-31 01:33:57 +08:00
Compare commits
4
Commits
main
...
py-fix-pr_3304
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a21a2e34d0 | ||
|
|
7a22b61969 | ||
|
|
477f85b97c | ||
|
|
523915f9fe |
@@ -0,0 +1,46 @@
|
||||
-- liquibase formatted sql
|
||||
|
||||
-- 插入新的 hass_state 插件记录(合并 get_state 的配置字段)
|
||||
INSERT INTO ai_model_provider (id, model_type, provider_code, name, fields,
|
||||
sort, creator, create_date, updater, update_date)
|
||||
VALUES ('SYSTEM_PLUGIN_HA_STATE',
|
||||
'Plugin',
|
||||
'hass_state',
|
||||
'HomeAssistant设备控制',
|
||||
JSON_ARRAY(
|
||||
JSON_OBJECT(
|
||||
'key', 'base_url',
|
||||
'type', 'string',
|
||||
'label', 'HA 服务器地址',
|
||||
'default',
|
||||
(SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.base_url')
|
||||
),
|
||||
JSON_OBJECT(
|
||||
'key', 'api_key',
|
||||
'type', 'string',
|
||||
'label', 'HA API 访问令牌',
|
||||
'default',
|
||||
(SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.api_key')
|
||||
),
|
||||
JSON_OBJECT(
|
||||
'key', 'devices',
|
||||
'type', 'array',
|
||||
'label', '设备列表(名称,实体ID;…)',
|
||||
'default',
|
||||
(SELECT param_value FROM sys_params WHERE param_code = 'plugins.home_assistant.devices')
|
||||
)
|
||||
),
|
||||
50, 0, NOW(), 0, NOW());
|
||||
|
||||
-- 迁移已配置的 agent:把指向 HA_GET_STATE 的改成 HA_STATE(保留参数)
|
||||
UPDATE ai_agent_plugin_mapping
|
||||
SET plugin_id = 'SYSTEM_PLUGIN_HA_STATE'
|
||||
WHERE plugin_id = 'SYSTEM_PLUGIN_HA_GET_STATE';
|
||||
|
||||
-- HA_SET_STATE 本来就没有配置字段(fields=[]),直接删除其 mapping
|
||||
DELETE FROM ai_agent_plugin_mapping
|
||||
WHERE plugin_id = 'SYSTEM_PLUGIN_HA_SET_STATE';
|
||||
|
||||
-- 删除旧的插件定义
|
||||
DELETE FROM ai_model_provider
|
||||
WHERE id IN ('SYSTEM_PLUGIN_HA_GET_STATE', 'SYSTEM_PLUGIN_HA_SET_STATE');
|
||||
@@ -711,3 +711,10 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202607101200.sql
|
||||
- changeSet:
|
||||
id: 202607290930
|
||||
author: cgd
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202607290930.sql
|
||||
|
||||
@@ -497,8 +497,7 @@ const FALLBACK_PLUGIN_NAME_KEYS = {
|
||||
SYSTEM_PLUGIN_MUSIC: "agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC",
|
||||
SYSTEM_PLUGIN_NEWS_CHINANEWS: "agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS",
|
||||
SYSTEM_PLUGIN_NEWS_NEWSNOW: "agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW",
|
||||
SYSTEM_PLUGIN_HA_GET_STATE: "agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE",
|
||||
SYSTEM_PLUGIN_HA_SET_STATE: "agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE",
|
||||
SYSTEM_PLUGIN_HA_STATE: "agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE",
|
||||
SYSTEM_PLUGIN_HA_PLAY_MUSIC: "agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC",
|
||||
SYSTEM_PLUGIN_WEB_SEARCH: "agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH",
|
||||
SYSTEM_PLUGIN_CALL_DEVICE: "agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE"
|
||||
|
||||
@@ -903,8 +903,7 @@ export default {
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC': 'Server-Musikplayer',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS': 'China News',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW': 'NewsNow-Aggregation',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE': 'HomeAssistant-Statusabfrage',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE': 'HomeAssistant-Statusaktualisierung',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE': 'HomeAssistant-Gerätesteuerung',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC': 'HomeAssistant-Musikplayer',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH': 'Websuche',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE': 'Geräteanruf',
|
||||
|
||||
@@ -953,8 +953,7 @@ export default {
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC': 'Server music player',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS': 'China News',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW': 'NewsNow aggregation',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE': 'HomeAssistant state query',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE': 'HomeAssistant state update',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE': 'HomeAssistant device control',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC': 'HomeAssistant music player',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH': 'Web search',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE': 'Device-to-device call',
|
||||
|
||||
@@ -903,8 +903,7 @@ export default {
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC': 'Reprodutor de música do servidor',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS': 'China News',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW': 'Agregação NewsNow',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE': 'Consulta de estado do HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE': 'Atualização de estado do HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE': 'Controle de dispositivo do HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC': 'Reprodutor de música do HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH': 'Pesquisa na web',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE': 'Chamada entre dispositivos',
|
||||
|
||||
@@ -903,8 +903,7 @@ export default {
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC': 'Trình phát nhạc máy chủ',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS': 'Tin tức China News',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW': 'Tổng hợp NewsNow',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE': 'Truy vấn trạng thái HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE': 'Cập nhật trạng thái HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE': 'Điều khiển thiết bị HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC': 'Trình phát nhạc HomeAssistant',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH': 'Tìm kiếm web',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE': 'Gọi thiết bị',
|
||||
|
||||
@@ -953,8 +953,7 @@ export default {
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC': '服务器音乐播放',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS': '中新网新闻',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW': 'newsnow新闻聚合',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE': 'HomeAssistant设备状态查询',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE': 'HomeAssistant设备状态修改',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE': 'HomeAssistant设备控制',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC': 'HomeAssistant音乐播放',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH': '联网搜索',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE': '设备呼叫设备',
|
||||
|
||||
@@ -903,8 +903,7 @@ export default {
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_MUSIC': '伺服器音樂播放',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_CHINANEWS': '中新網新聞',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_NEWS_NEWSNOW': 'newsnow新聞聚合',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_GET_STATE': 'HomeAssistant設備狀態查詢',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_SET_STATE': 'HomeAssistant設備狀態修改',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_STATE': 'HomeAssistant設備控制',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_HA_PLAY_MUSIC': 'HomeAssistant音樂播放',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_WEB_SEARCH': '聯網搜尋',
|
||||
'agentSnapshot.plugin.SYSTEM_PLUGIN_CALL_DEVICE': '設備呼叫設備',
|
||||
|
||||
@@ -286,8 +286,7 @@ Intent:
|
||||
# play_music是服务器自带的音乐播放,hass_play_music是通过home assistant控制的独立外部程序音乐播放
|
||||
# 如果用了hass_play_music,就不要开启play_music,两者只留一个
|
||||
- play_music
|
||||
#- hass_get_state
|
||||
#- hass_set_state
|
||||
#- hass_state
|
||||
#- hass_play_music
|
||||
|
||||
Memory:
|
||||
|
||||
@@ -35,7 +35,7 @@ from core.providers.asr.dto.dto import InterfaceType
|
||||
from core.handle.textHandle import handleTextMessage
|
||||
from core.providers.tools.unified_tool_handler import UnifiedToolHandler
|
||||
from plugins_func.loadplugins import auto_import_modules
|
||||
from plugins_func.register import Action, ActionResponse
|
||||
from plugins_func.register import Action, ActionResponse, all_function_registry, module_func_map
|
||||
from core.auth import AuthenticationError
|
||||
from config.config_loader import get_private_config_from_api
|
||||
from core.providers.tts.dto.dto import ContentType, TTSMessageDTO, SentenceType
|
||||
@@ -878,10 +878,27 @@ class ConnectionHandler:
|
||||
plugin_from_server = private_config.get("plugins", {})
|
||||
for plugin, config_str in plugin_from_server.items():
|
||||
plugin_from_server[plugin] = json.loads(config_str)
|
||||
# 将模块级别的插件配置复制到各具体函数名,
|
||||
# 方便后续 per-function 查找描述、news_sources 等配置
|
||||
for module_name, func_names in module_func_map.items():
|
||||
if module_name in plugin_from_server:
|
||||
module_config = plugin_from_server[module_name]
|
||||
for func_name in func_names:
|
||||
if func_name not in plugin_from_server:
|
||||
plugin_from_server[func_name] = module_config
|
||||
self.config["plugins"] = plugin_from_server
|
||||
# 将模块级别的插件名展开为具体函数名
|
||||
expanded_functions = []
|
||||
for plugin_key in plugin_from_server.keys():
|
||||
if plugin_key in all_function_registry:
|
||||
expanded_functions.append(plugin_key)
|
||||
elif plugin_key in module_func_map:
|
||||
expanded_functions.extend(module_func_map[plugin_key])
|
||||
else:
|
||||
expanded_functions.append(plugin_key)
|
||||
self.config["Intent"][self.config["selected_module"]["Intent"]][
|
||||
"functions"
|
||||
] = plugin_from_server.keys()
|
||||
] = expanded_functions
|
||||
if private_config.get("prompt", None) is not None:
|
||||
self.config["prompt"] = private_config["prompt"]
|
||||
# 获取声纹信息
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Dict, Any, TYPE_CHECKING
|
||||
if TYPE_CHECKING:
|
||||
from core.connection import ConnectionHandler
|
||||
from ..base import ToolType, ToolDefinition, ToolExecutor
|
||||
from plugins_func.register import all_function_registry, Action, ActionResponse
|
||||
from plugins_func.register import all_function_registry, module_func_map, Action, ActionResponse
|
||||
|
||||
|
||||
class ServerPluginExecutor(ToolExecutor):
|
||||
@@ -54,6 +54,43 @@ class ServerPluginExecutor(ToolExecutor):
|
||||
response=str(e),
|
||||
)
|
||||
|
||||
def _expand_plugin_names(self, config_functions):
|
||||
"""将模块级别的插件名展开为具体函数名。
|
||||
|
||||
在一个 function 文件中可能注册多个 @register_function,
|
||||
配置中如果使用的是模块名(文件名),需要展开为具体的函数名列表。
|
||||
"""
|
||||
if not isinstance(config_functions, list):
|
||||
try:
|
||||
config_functions = list(config_functions)
|
||||
except TypeError:
|
||||
return []
|
||||
|
||||
expanded = []
|
||||
for name in config_functions:
|
||||
if name in module_func_map:
|
||||
# 模块名,展开为该模块下所有注册函数名
|
||||
expanded.extend(module_func_map[name])
|
||||
elif name in all_function_registry:
|
||||
# 精确匹配函数名,直接保留
|
||||
expanded.append(name)
|
||||
else:
|
||||
# 未知名称,保留原值(可能是 MCP 或其他工具)
|
||||
expanded.append(name)
|
||||
return expanded
|
||||
|
||||
def _get_plugin_description(self, func_name):
|
||||
"""获取插件函数的描述,优先精确匹配函数名,其次匹配模块名。"""
|
||||
plugins = self.config.get("plugins", {})
|
||||
# 精确匹配函数名
|
||||
if func_name in plugins:
|
||||
return plugins[func_name].get("description", "")
|
||||
# 通过 module_func_map 反向查找模块名
|
||||
for module_name, func_names in module_func_map.items():
|
||||
if func_name in func_names and module_name in plugins:
|
||||
return plugins[module_name].get("description", "")
|
||||
return ""
|
||||
|
||||
def get_tools(self) -> Dict[str, ToolDefinition]:
|
||||
"""获取所有注册的服务端插件工具"""
|
||||
tools = {}
|
||||
@@ -66,12 +103,8 @@ class ServerPluginExecutor(ToolExecutor):
|
||||
self.config["selected_module"]["Intent"]
|
||||
].get("functions", [])
|
||||
|
||||
# 转换为列表
|
||||
if not isinstance(config_functions, list):
|
||||
try:
|
||||
config_functions = list(config_functions)
|
||||
except TypeError:
|
||||
config_functions = []
|
||||
# 将模块级别的插件名展开为具体函数名(兜底机制)
|
||||
config_functions = self._expand_plugin_names(config_functions)
|
||||
|
||||
# 合并所有需要的函数
|
||||
all_required_functions = list(set(necessary_functions + config_functions))
|
||||
@@ -79,12 +112,8 @@ class ServerPluginExecutor(ToolExecutor):
|
||||
for func_name in all_required_functions:
|
||||
func_item = all_function_registry.get(func_name)
|
||||
if func_item:
|
||||
# 从函数注册中获取描述
|
||||
fun_description = (
|
||||
self.config.get("plugins", {})
|
||||
.get(func_name, {})
|
||||
.get("description", "")
|
||||
)
|
||||
# 从函数注册中获取描述(支持模块名和函数名的双层查找)
|
||||
fun_description = self._get_plugin_description(func_name)
|
||||
if fun_description is not None and len(fun_description) > 0:
|
||||
if "function" in func_item.description and isinstance(
|
||||
func_item.description["function"], dict
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import httpx
|
||||
from config.logger import setup_logging
|
||||
from plugins_func.functions.hass_init import initialize_hass_handler
|
||||
from plugins_func.register import register_function, ToolType, ActionResponse, Action
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from core.connection import ConnectionHandler
|
||||
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
hass_get_state_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "hass_get_state",
|
||||
"description": "获取homeassistant里设备的状态,包括查询灯光亮度、颜色、色温,媒体播放器的音量,设备的暂停、继续操作",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity_id": {
|
||||
"type": "string",
|
||||
"description": "需要操作的设备id,homeassistant里的entity_id",
|
||||
}
|
||||
},
|
||||
"required": ["entity_id"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL)
|
||||
async def hass_get_state(conn: "ConnectionHandler", entity_id=""):
|
||||
try:
|
||||
ha_response = await handle_hass_get_state(conn, entity_id)
|
||||
return ActionResponse(Action.REQLLM, ha_response, None)
|
||||
except httpx.TimeoutException:
|
||||
logger.bind(tag=TAG).error("获取Home Assistant状态超时")
|
||||
return ActionResponse(Action.ERROR, "请求超时", None)
|
||||
except Exception as e:
|
||||
error_msg = f"执行Home Assistant操作失败"
|
||||
logger.bind(tag=TAG).error(error_msg)
|
||||
return ActionResponse(Action.ERROR, error_msg, None)
|
||||
|
||||
|
||||
async def handle_hass_get_state(conn: "ConnectionHandler", entity_id):
|
||||
ha_config = initialize_hass_handler(conn)
|
||||
api_key = ha_config.get("api_key")
|
||||
base_url = ha_config.get("base_url")
|
||||
url = f"{base_url}/api/states/{entity_id}"
|
||||
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||
|
||||
async with httpx.AsyncClient(timeout=httpx.Timeout(5.0, connect=3.0)) as client:
|
||||
response = await client.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
responsetext = "设备状态:" + response.json()["state"] + " "
|
||||
logger.bind(tag=TAG).info(f"api返回内容: {response.json()}")
|
||||
|
||||
if "media_title" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "正在播放的是:"
|
||||
+ str(response.json()["attributes"]["media_title"])
|
||||
+ " "
|
||||
)
|
||||
if "volume_level" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "音量是:"
|
||||
+ str(response.json()["attributes"]["volume_level"])
|
||||
+ " "
|
||||
)
|
||||
if "color_temp_kelvin" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "色温是:"
|
||||
+ str(response.json()["attributes"]["color_temp_kelvin"])
|
||||
+ " "
|
||||
)
|
||||
if "rgb_color" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "rgb颜色是:"
|
||||
+ str(response.json()["attributes"]["rgb_color"])
|
||||
+ " "
|
||||
)
|
||||
if "brightness" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "亮度是:"
|
||||
+ str(response.json()["attributes"]["brightness"])
|
||||
+ " "
|
||||
)
|
||||
logger.bind(tag=TAG).info(f"查询返回内容: {responsetext}")
|
||||
return responsetext
|
||||
else:
|
||||
return f"切换失败,错误码: {response.status_code}"
|
||||
@@ -16,7 +16,7 @@ def append_devices_to_prompt(conn):
|
||||
config_source = (
|
||||
"home_assistant"
|
||||
if plugins_config.get("home_assistant")
|
||||
else "hass_get_state"
|
||||
else "hass_state"
|
||||
)
|
||||
|
||||
if "hass_get_state" in funcs or "hass_set_state" in funcs:
|
||||
@@ -36,7 +36,7 @@ def initialize_hass_handler(conn):
|
||||
plugins_config = conn.config.get("plugins", {})
|
||||
# 确定配置来源
|
||||
config_source = (
|
||||
"home_assistant" if plugins_config.get("home_assistant") else "hass_get_state"
|
||||
"home_assistant" if plugins_config.get("home_assistant") else "hass_state"
|
||||
)
|
||||
if not plugins_config.get(config_source):
|
||||
return ha_config
|
||||
|
||||
+87
@@ -10,6 +10,24 @@ if TYPE_CHECKING:
|
||||
TAG = __name__
|
||||
logger = setup_logging()
|
||||
|
||||
hass_get_state_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "hass_get_state",
|
||||
"description": "获取homeassistant里设备的状态,包括查询灯光亮度、颜色、色温,媒体播放器的音量,设备的暂停、继续操作",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"entity_id": {
|
||||
"type": "string",
|
||||
"description": "需要操作的设备id,homeassistant里的entity_id",
|
||||
}
|
||||
},
|
||||
"required": ["entity_id"],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
hass_set_state_function_desc = {
|
||||
"type": "function",
|
||||
"function": {
|
||||
@@ -52,6 +70,20 @@ hass_set_state_function_desc = {
|
||||
}
|
||||
|
||||
|
||||
@register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL)
|
||||
async def hass_get_state(conn: "ConnectionHandler", entity_id=""):
|
||||
try:
|
||||
ha_response = await handle_hass_get_state(conn, entity_id)
|
||||
return ActionResponse(Action.REQLLM, ha_response, None)
|
||||
except httpx.TimeoutException:
|
||||
logger.bind(tag=TAG).error("获取Home Assistant状态超时")
|
||||
return ActionResponse(Action.ERROR, "请求超时", None)
|
||||
except Exception as e:
|
||||
error_msg = f"执行Home Assistant操作失败"
|
||||
logger.bind(tag=TAG).error(error_msg)
|
||||
return ActionResponse(Action.ERROR, error_msg, None)
|
||||
|
||||
|
||||
@register_function("hass_set_state", hass_set_state_function_desc, ToolType.SYSTEM_CTL)
|
||||
async def hass_set_state(conn: "ConnectionHandler", entity_id="", state=None):
|
||||
if state is None:
|
||||
@@ -68,6 +100,61 @@ async def hass_set_state(conn: "ConnectionHandler", entity_id="", state=None):
|
||||
return ActionResponse(Action.ERROR, error_msg, None)
|
||||
|
||||
|
||||
async def handle_hass_get_state(conn: "ConnectionHandler", entity_id):
|
||||
ha_config = initialize_hass_handler(conn)
|
||||
api_key = ha_config.get("api_key")
|
||||
base_url = ha_config.get("base_url")
|
||||
url = f"{base_url}/api/states/{entity_id}"
|
||||
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||
|
||||
async with httpx.AsyncClient(timeout=httpx.Timeout(5.0, connect=3.0)) as client:
|
||||
response = await client.get(url, headers=headers)
|
||||
|
||||
if response.status_code == 200:
|
||||
responsetext = "设备状态:" + response.json()["state"] + " "
|
||||
logger.bind(tag=TAG).info(f"api返回内容: {response.json()}")
|
||||
|
||||
if "media_title" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "正在播放的是:"
|
||||
+ str(response.json()["attributes"]["media_title"])
|
||||
+ " "
|
||||
)
|
||||
if "volume_level" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "音量是:"
|
||||
+ str(response.json()["attributes"]["volume_level"])
|
||||
+ " "
|
||||
)
|
||||
if "color_temp_kelvin" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "色温是:"
|
||||
+ str(response.json()["attributes"]["color_temp_kelvin"])
|
||||
+ " "
|
||||
)
|
||||
if "rgb_color" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "rgb颜色是:"
|
||||
+ str(response.json()["attributes"]["rgb_color"])
|
||||
+ " "
|
||||
)
|
||||
if "brightness" in response.json()["attributes"]:
|
||||
responsetext = (
|
||||
responsetext
|
||||
+ "亮度是:"
|
||||
+ str(response.json()["attributes"]["brightness"])
|
||||
+ " "
|
||||
)
|
||||
logger.bind(tag=TAG).info(f"查询返回内容: {responsetext}")
|
||||
return responsetext
|
||||
else:
|
||||
return f"切换失败,错误码: {response.status_code}"
|
||||
|
||||
|
||||
async def handle_hass_set_state(conn: "ConnectionHandler", entity_id, state):
|
||||
ha_config = initialize_hass_handler(conn)
|
||||
api_key = ha_config.get("api_key")
|
||||
@@ -78,6 +78,8 @@ class DeviceTypeRegistry:
|
||||
|
||||
# 初始化函数注册字典
|
||||
all_function_registry = {}
|
||||
# 模块名 -> 函数名列表的映射,用于将模块级别的插件名展开为具体的函数名
|
||||
module_func_map = {}
|
||||
|
||||
|
||||
def register_function(name, desc, type=None):
|
||||
@@ -85,6 +87,9 @@ def register_function(name, desc, type=None):
|
||||
|
||||
def decorator(func):
|
||||
all_function_registry[name] = FunctionItem(name, desc, func, type)
|
||||
# 记录模块名到函数名的映射,用于 expand 模块级别的插件配置
|
||||
module_name = func.__module__.split(".")[-1]
|
||||
module_func_map.setdefault(module_name, []).append(name)
|
||||
logger.bind(tag=TAG).debug(f"函数 '{name}' 已加载,可以注册使用")
|
||||
return func
|
||||
|
||||
|
||||
Reference in New Issue
Block a user