mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-28 01:53:53 +08:00
update:优化ha工具获取密钥的方式
This commit is contained in:
@@ -17,57 +17,78 @@ hass_get_state_function_desc = {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"entity_id": {
|
"entity_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "需要操作的设备id,homeassistant里的entity_id"
|
"description": "需要操作的设备id,homeassistant里的entity_id",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": ["entity_id"]
|
"required": ["entity_id"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL)
|
@register_function("hass_get_state", hass_get_state_function_desc, ToolType.SYSTEM_CTL)
|
||||||
def hass_get_state(conn, entity_id=''):
|
def hass_get_state(conn, entity_id=""):
|
||||||
try:
|
try:
|
||||||
|
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
handle_hass_get_state(conn, entity_id),
|
handle_hass_get_state(conn, entity_id), conn.loop
|
||||||
conn.loop
|
|
||||||
)
|
)
|
||||||
ha_response = future.result()
|
ha_response = future.result()
|
||||||
return ActionResponse( Action.REQLLM, ha_response , None )
|
return ActionResponse(Action.REQLLM, ha_response, None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
|
logger.bind(tag=TAG).error(f"处理设置属性意图错误: {e}")
|
||||||
|
|
||||||
|
|
||||||
async def handle_hass_get_state(conn, entity_id):
|
async def handle_hass_get_state(conn, entity_id):
|
||||||
HASS_CACHE = initialize_hass_handler(conn)
|
ha_config = initialize_hass_handler(conn)
|
||||||
api_key = HASS_CACHE['api_key']
|
api_key = ha_config.get("api_key")
|
||||||
base_url = HASS_CACHE['base_url']
|
base_url = ha_config.get("base_url")
|
||||||
url = f"{base_url}/api/states/{entity_id}"
|
url = f"{base_url}/api/states/{entity_id}"
|
||||||
headers = {
|
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||||
"Authorization": f"Bearer {api_key}",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
response = requests.get(url, headers=headers)
|
response = requests.get(url, headers=headers)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
responsetext = '设备状态:' + response.json()['state'] + ' '
|
responsetext = "设备状态:" + response.json()["state"] + " "
|
||||||
logger.bind(tag=TAG).info(f"api返回内容: {response.json()}")
|
logger.bind(tag=TAG).info(f"api返回内容: {response.json()}")
|
||||||
|
|
||||||
if 'media_title' in response.json()['attributes']:
|
if "media_title" in response.json()["attributes"]:
|
||||||
responsetext = responsetext+ '正在播放的是:'+str(response.json()['attributes']['media_title'])+' '
|
responsetext = (
|
||||||
if 'volume_level' in response.json()['attributes']:
|
responsetext
|
||||||
responsetext = responsetext+ '音量是:'+str(response.json()['attributes']['volume_level'])+' '
|
+ "正在播放的是:"
|
||||||
if 'color_temp_kelvin' in response.json()['attributes']:
|
+ str(response.json()["attributes"]["media_title"])
|
||||||
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 "volume_level" in response.json()["attributes"]:
|
||||||
if 'brightness' in response.json()['attributes']:
|
responsetext = (
|
||||||
responsetext = responsetext+ '亮度是:'+str(response.json()['attributes']['brightness'])+' '
|
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}")
|
logger.bind(tag=TAG).info(f"查询返回内容: {responsetext}")
|
||||||
return responsetext
|
return responsetext
|
||||||
#return response.json()['attributes']
|
# return response.json()['attributes']
|
||||||
#response.attributes
|
# response.attributes
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return f"切换失败,错误码: {response.status_code}"
|
return f"切换失败,错误码: {response.status_code}"
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ from core.utils.util import check_model_key
|
|||||||
TAG = __name__
|
TAG = __name__
|
||||||
logger = setup_logging()
|
logger = setup_logging()
|
||||||
|
|
||||||
HASS_CACHE = {}
|
|
||||||
|
|
||||||
|
|
||||||
def append_devices_to_prompt(conn):
|
def append_devices_to_prompt(conn):
|
||||||
if conn.intent_type == "function_call":
|
if conn.intent_type == "function_call":
|
||||||
@@ -25,19 +23,22 @@ def append_devices_to_prompt(conn):
|
|||||||
|
|
||||||
|
|
||||||
def initialize_hass_handler(conn):
|
def initialize_hass_handler(conn):
|
||||||
global HASS_CACHE
|
ha_config = {}
|
||||||
if HASS_CACHE == {}:
|
if conn.load_function_plugin:
|
||||||
if conn.load_function_plugin:
|
if conn.config["plugins"].get("home_assistant"):
|
||||||
funcs = conn.config["Intent"][conn.config["selected_module"]["Intent"]].get(
|
ha_config["base_url"] = conn.config["plugins"]["home_assistant"].get(
|
||||||
"functions", []
|
"base_url"
|
||||||
)
|
)
|
||||||
if "hass_get_state" in funcs or "hass_set_state" in funcs:
|
ha_config["api_key"] = conn.config["plugins"]["home_assistant"].get(
|
||||||
HASS_CACHE["base_url"] = conn.config["plugins"]["home_assistant"].get(
|
"api_key"
|
||||||
"base_url"
|
)
|
||||||
)
|
check_model_key("home_assistant", ha_config.get("api_key"))
|
||||||
HASS_CACHE["api_key"] = conn.config["plugins"]["home_assistant"].get(
|
elif conn.config["plugins"].get("hass_get_state"):
|
||||||
"api_key"
|
ha_config["base_url"] = conn.config["plugins"]["hass_get_state"].get(
|
||||||
)
|
"base_url"
|
||||||
|
)
|
||||||
check_model_key("home_assistant", HASS_CACHE["api_key"])
|
ha_config["api_key"] = conn.config["plugins"]["hass_get_state"].get(
|
||||||
return HASS_CACHE
|
"api_key"
|
||||||
|
)
|
||||||
|
check_model_key("home_assistant", ha_config.get("api_key"))
|
||||||
|
return ha_config
|
||||||
|
|||||||
@@ -17,46 +17,43 @@ hass_play_music_function_desc = {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"media_content_id": {
|
"media_content_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "可以是音乐或有声书的专辑名称、歌曲名、演唱者,如果未指定就填random"
|
"description": "可以是音乐或有声书的专辑名称、歌曲名、演唱者,如果未指定就填random",
|
||||||
},
|
},
|
||||||
"entity_id": {
|
"entity_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "需要操作的音箱的设备id,homeassistant里的entity_id,media_player开头"
|
"description": "需要操作的音箱的设备id,homeassistant里的entity_id,media_player开头",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"required": ["media_content_id", "entity_id"]
|
"required": ["media_content_id", "entity_id"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@register_function('hass_play_music', hass_play_music_function_desc, ToolType.SYSTEM_CTL)
|
@register_function(
|
||||||
def hass_play_music(conn, entity_id='', media_content_id='random'):
|
"hass_play_music", hass_play_music_function_desc, ToolType.SYSTEM_CTL
|
||||||
|
)
|
||||||
|
def hass_play_music(conn, entity_id="", media_content_id="random"):
|
||||||
try:
|
try:
|
||||||
# 执行音乐播放命令
|
# 执行音乐播放命令
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
handle_hass_play_music(conn, entity_id, media_content_id),
|
handle_hass_play_music(conn, entity_id, media_content_id), conn.loop
|
||||||
conn.loop
|
|
||||||
)
|
)
|
||||||
ha_response = future.result()
|
ha_response = future.result()
|
||||||
return ActionResponse(action=Action.RESPONSE, result="退出意图已处理", response=ha_response)
|
return ActionResponse(
|
||||||
|
action=Action.RESPONSE, result="退出意图已处理", response=ha_response
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}")
|
logger.bind(tag=TAG).error(f"处理音乐意图错误: {e}")
|
||||||
|
|
||||||
|
|
||||||
async def handle_hass_play_music(conn, entity_id, media_content_id):
|
async def handle_hass_play_music(conn, entity_id, media_content_id):
|
||||||
HASS_CACHE = initialize_hass_handler(conn)
|
ha_config = initialize_hass_handler(conn)
|
||||||
api_key = HASS_CACHE['api_key']
|
api_key = ha_config.get("api_key")
|
||||||
base_url = HASS_CACHE['base_url']
|
base_url = ha_config.get("base_url")
|
||||||
url = f"{base_url}/api/services/music_assistant/play_media"
|
url = f"{base_url}/api/services/music_assistant/play_media"
|
||||||
headers = {
|
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||||
"Authorization": f"Bearer {api_key}",
|
data = {"entity_id": entity_id, "media_id": media_content_id}
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
data = {
|
|
||||||
"entity_id": entity_id,
|
|
||||||
"media_id": media_content_id
|
|
||||||
}
|
|
||||||
response = requests.post(url, headers=headers, json=data)
|
response = requests.post(url, headers=headers, json=data)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return f"正在播放{media_content_id}的音乐"
|
return f"正在播放{media_content_id}的音乐"
|
||||||
|
|||||||
@@ -20,40 +20,39 @@ hass_set_state_function_desc = {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "需要操作的动作,打开设备:turn_on,关闭设备:turn_off,增加亮度:brightness_up,降低亮度:brightness_down,设置亮度:brightness_value,增加音量:volume_up,降低音量:volume_down,设置音量:volume_set,设置色温:set_kelvin,设置颜色:set_color,设备暂停:pause,设备继续:continue,静音/取消静音:volume_mute"
|
"description": "需要操作的动作,打开设备:turn_on,关闭设备:turn_off,增加亮度:brightness_up,降低亮度:brightness_down,设置亮度:brightness_value,增加音量:volume_up,降低音量:volume_down,设置音量:volume_set,设置色温:set_kelvin,设置颜色:set_color,设备暂停:pause,设备继续:continue,静音/取消静音:volume_mute",
|
||||||
},
|
},
|
||||||
"input": {
|
"input": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"description": "只有在设置音量,设置亮度时候才需要,有效值为1-100,对应音量和亮度的1%-100%"
|
"description": "只有在设置音量,设置亮度时候才需要,有效值为1-100,对应音量和亮度的1%-100%",
|
||||||
},
|
},
|
||||||
"is_muted": {
|
"is_muted": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "只有在设置静音操作时才需要,设置静音的时候该值为true,取消静音时该值为false"
|
"description": "只有在设置静音操作时才需要,设置静音的时候该值为true,取消静音时该值为false",
|
||||||
|
},
|
||||||
|
"rgb_color": {
|
||||||
|
"type": "list",
|
||||||
|
"description": "只有在设置颜色时需要,这里填目标颜色的rgb值",
|
||||||
},
|
},
|
||||||
"rgb_color":{
|
|
||||||
"type":"list",
|
|
||||||
"description": "只有在设置颜色时需要,这里填目标颜色的rgb值"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"required": ["type"]
|
"required": ["type"],
|
||||||
},
|
},
|
||||||
"entity_id": {
|
"entity_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "需要操作的设备id,homeassistant里的entity_id"
|
"description": "需要操作的设备id,homeassistant里的entity_id",
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
"required": ["state", "entity_id"]
|
"required": ["state", "entity_id"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@register_function('hass_set_state', hass_set_state_function_desc, ToolType.SYSTEM_CTL)
|
@register_function("hass_set_state", hass_set_state_function_desc, ToolType.SYSTEM_CTL)
|
||||||
def hass_set_state(conn, entity_id='', state={}):
|
def hass_set_state(conn, entity_id="", state={}):
|
||||||
try:
|
try:
|
||||||
future = asyncio.run_coroutine_threadsafe(
|
future = asyncio.run_coroutine_threadsafe(
|
||||||
handle_hass_set_state(conn, entity_id, state),
|
handle_hass_set_state(conn, entity_id, state), conn.loop
|
||||||
conn.loop
|
|
||||||
)
|
)
|
||||||
ha_response = future.result()
|
ha_response = future.result()
|
||||||
return ActionResponse(Action.REQLLM, ha_response, None)
|
return ActionResponse(Action.REQLLM, ha_response, None)
|
||||||
@@ -62,21 +61,21 @@ def hass_set_state(conn, entity_id='', state={}):
|
|||||||
|
|
||||||
|
|
||||||
async def handle_hass_set_state(conn, entity_id, state):
|
async def handle_hass_set_state(conn, entity_id, state):
|
||||||
HASS_CACHE = initialize_hass_handler(conn)
|
ha_config = initialize_hass_handler(conn)
|
||||||
api_key = HASS_CACHE['api_key']
|
api_key = ha_config.get("api_key")
|
||||||
base_url = HASS_CACHE['base_url']
|
base_url = ha_config.get("base_url")
|
||||||
'''
|
"""
|
||||||
state = { "type":"brightness_up","input":"80","is_muted":"true"}
|
state = { "type":"brightness_up","input":"80","is_muted":"true"}
|
||||||
'''
|
"""
|
||||||
domains = entity_id.split(".")
|
domains = entity_id.split(".")
|
||||||
if len(domains) > 1:
|
if len(domains) > 1:
|
||||||
domain = domains[0]
|
domain = domains[0]
|
||||||
else:
|
else:
|
||||||
return "执行失败,错误的设备id"
|
return "执行失败,错误的设备id"
|
||||||
action = ''
|
action = ""
|
||||||
arg = ''
|
arg = ""
|
||||||
value = ''
|
value = ""
|
||||||
if state['type'] == 'turn_on':
|
if state["type"] == "turn_on":
|
||||||
description = "设备已打开"
|
description = "设备已打开"
|
||||||
if domain == "cover":
|
if domain == "cover":
|
||||||
action = "open_cover"
|
action = "open_cover"
|
||||||
@@ -84,91 +83,87 @@ async def handle_hass_set_state(conn, entity_id, state):
|
|||||||
action = "start"
|
action = "start"
|
||||||
else:
|
else:
|
||||||
action = "turn_on"
|
action = "turn_on"
|
||||||
elif state['type'] == 'turn_off':
|
elif state["type"] == "turn_off":
|
||||||
description = "设备已关闭"
|
description = "设备已关闭"
|
||||||
if domain == 'cover':
|
if domain == "cover":
|
||||||
action = "close_cover"
|
action = "close_cover"
|
||||||
elif domain == 'vacuum':
|
elif domain == "vacuum":
|
||||||
action = "stop"
|
action = "stop"
|
||||||
else:
|
else:
|
||||||
action = "turn_off"
|
action = "turn_off"
|
||||||
elif state['type'] == 'brightness_up':
|
elif state["type"] == "brightness_up":
|
||||||
description = "灯光已调亮"
|
description = "灯光已调亮"
|
||||||
action = 'turn_on'
|
action = "turn_on"
|
||||||
arg = 'brightness_step_pct'
|
arg = "brightness_step_pct"
|
||||||
value = 10
|
value = 10
|
||||||
elif state['type'] == 'brightness_down':
|
elif state["type"] == "brightness_down":
|
||||||
description = "灯光已调暗"
|
description = "灯光已调暗"
|
||||||
action = 'turn_on'
|
action = "turn_on"
|
||||||
arg = 'brightness_step_pct'
|
arg = "brightness_step_pct"
|
||||||
value = -10
|
value = -10
|
||||||
elif state['type'] == 'brightness_value':
|
elif state["type"] == "brightness_value":
|
||||||
description = f"亮度已调整到{state['input']}"
|
description = f"亮度已调整到{state['input']}"
|
||||||
action = 'turn_on'
|
action = "turn_on"
|
||||||
arg = 'brightness_pct'
|
arg = "brightness_pct"
|
||||||
value = state['input']
|
value = state["input"]
|
||||||
elif state['type'] == 'set_color':
|
elif state["type"] == "set_color":
|
||||||
description = f"颜色已调整到{state['rgb_color']}"
|
description = f"颜色已调整到{state['rgb_color']}"
|
||||||
action = 'turn_on'
|
action = "turn_on"
|
||||||
arg = 'rgb_color'
|
arg = "rgb_color"
|
||||||
value = state['rgb_color']
|
value = state["rgb_color"]
|
||||||
elif state['type'] == 'set_kelvin':
|
elif state["type"] == "set_kelvin":
|
||||||
description = f"色温已调整到{state['input']}K"
|
description = f"色温已调整到{state['input']}K"
|
||||||
action = 'turn_on'
|
action = "turn_on"
|
||||||
arg = 'kelvin'
|
arg = "kelvin"
|
||||||
value = state['input']
|
value = state["input"]
|
||||||
elif state['type'] == 'volume_up':
|
elif state["type"] == "volume_up":
|
||||||
description = "音量已调大"
|
description = "音量已调大"
|
||||||
action = state['type']
|
action = state["type"]
|
||||||
elif state['type'] == 'volume_down':
|
elif state["type"] == "volume_down":
|
||||||
description = "音量已调小"
|
description = "音量已调小"
|
||||||
action = state['type']
|
action = state["type"]
|
||||||
elif state['type'] == 'volume_set':
|
elif state["type"] == "volume_set":
|
||||||
description = f"音量已调整到{state['input']}"
|
description = f"音量已调整到{state['input']}"
|
||||||
action = state['type']
|
action = state["type"]
|
||||||
arg = 'volume_level'
|
arg = "volume_level"
|
||||||
value = state['input']
|
value = state["input"]
|
||||||
if state['input'] >= 1:
|
if state["input"] >= 1:
|
||||||
value = state['input']/100
|
value = state["input"] / 100
|
||||||
elif state['type'] == 'volume_mute':
|
elif state["type"] == "volume_mute":
|
||||||
description = f"设备已静音"
|
description = f"设备已静音"
|
||||||
action = state['type']
|
action = state["type"]
|
||||||
arg = 'is_volume_muted'
|
arg = "is_volume_muted"
|
||||||
value = state['is_muted']
|
value = state["is_muted"]
|
||||||
elif state['type'] == 'pause':
|
elif state["type"] == "pause":
|
||||||
description = f"设备已暂停"
|
description = f"设备已暂停"
|
||||||
action = state['type']
|
action = state["type"]
|
||||||
if domain == 'media_player':
|
if domain == "media_player":
|
||||||
action = 'media_pause'
|
action = "media_pause"
|
||||||
if domain == 'cover':
|
if domain == "cover":
|
||||||
action = 'stop_cover'
|
action = "stop_cover"
|
||||||
if domain == 'vacuum':
|
if domain == "vacuum":
|
||||||
action = 'pause'
|
action = "pause"
|
||||||
elif state['type'] == 'continue':
|
elif state["type"] == "continue":
|
||||||
description = f"设备已继续"
|
description = f"设备已继续"
|
||||||
if domain == 'media_player':
|
if domain == "media_player":
|
||||||
action = 'media_play'
|
action = "media_play"
|
||||||
if domain == 'vacuum':
|
if domain == "vacuum":
|
||||||
action = 'start'
|
action = "start"
|
||||||
else:
|
else:
|
||||||
return f"{domain} {state.type}功能尚未支持"
|
return f"{domain} {state.type}功能尚未支持"
|
||||||
|
|
||||||
if arg == '':
|
if arg == "":
|
||||||
data = {
|
data = {
|
||||||
"entity_id": entity_id,
|
"entity_id": entity_id,
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
data = {
|
data = {"entity_id": entity_id, arg: value}
|
||||||
"entity_id": entity_id,
|
|
||||||
arg: value
|
|
||||||
}
|
|
||||||
url = f"{base_url}/api/services/{domain}/{action}"
|
url = f"{base_url}/api/services/{domain}/{action}"
|
||||||
headers = {
|
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
||||||
"Authorization": f"Bearer {api_key}",
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
response = requests.post(url, headers=headers, json=data)
|
response = requests.post(url, headers=headers, json=data)
|
||||||
logger.bind(tag=TAG).info(f"设置状态:{description},url:{url},return_code:{response.status_code}")
|
logger.bind(tag=TAG).info(
|
||||||
|
f"设置状态:{description},url:{url},return_code:{response.status_code}"
|
||||||
|
)
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
return description
|
return description
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user