Files
open-xiaoai/examples/hass/tests/test_ha_client.py
T
zimonianhua aafdf024d3 feat(hass): 新增小爱音箱接入 Home Assistant Assist 的完整示例
- 新增完整的 Home Assistant 集成示例,支持文本对话与上下文会话
- 实现小爱音箱事件解析与 TTS 播报,支持实时打断功能
- 添加连续会话管理,支持区域上下文注入和会话超时结束
- 提供完整的配置系统、类型定义和错误处理
- 包含 Rust 扩展模块用于小爱音箱通信,支持音频流处理
- 添加单元测试、集成测试脚本和 Docker 部署支持
- 提供详细的使用文档和配置说明
2026-03-04 17:11:04 +08:00

124 lines
3.8 KiB
Python

import json
import pytest
from aioresponses import aioresponses
from hass.errors import HomeAssistantAuthError
from hass.ha_client import HomeAssistantClient
from hass.memory import ConversationMemory
from hass.typing import HomeAssistantConfig
@pytest.mark.asyncio
async def test_process_text_updates_conversation_id(tmp_path):
state_path = tmp_path / "state.json"
memory = ConversationMemory(str(state_path))
await memory.load()
config = HomeAssistantConfig(
url="http://ha.local:8123",
access_token="token",
assistant_entity_id="home_assistant",
language="zh-cn",
timeout_seconds=2.5,
)
client = HomeAssistantClient(config, memory, concurrency=2)
await client.start()
url = "http://ha.local:8123/api/conversation/process"
with aioresponses() as mocked:
mocked.post(
url,
payload={
"continue_conversation": True,
"conversation_id": "cid-1",
"response": {
"response_type": "action_done",
"speech": {"plain": {"speech": "好的"}},
},
},
status=200,
)
reply = await client.process_text("打开客厅灯")
assert reply.speech == "好的"
assert reply.continue_conversation is True
assert memory.conversation_id == "cid-1"
await client.close()
@pytest.mark.asyncio
async def test_process_text_unauthorized(tmp_path):
memory = ConversationMemory(str(tmp_path / "state.json"))
await memory.load()
config = HomeAssistantConfig(url="http://ha.local:8123", access_token="bad-token")
client = HomeAssistantClient(config, memory)
await client.start()
url = "http://ha.local:8123/api/conversation/process"
with aioresponses() as mocked:
mocked.post(url, payload={"message": "unauthorized"}, status=401)
with pytest.raises(HomeAssistantAuthError):
await client.process_text("hi")
await client.close()
@pytest.mark.asyncio
async def test_process_text_with_prefix_prompt(tmp_path):
memory = ConversationMemory(str(tmp_path / "state.json"))
await memory.load()
config = HomeAssistantConfig(url="http://ha.local:8123", access_token="token")
client = HomeAssistantClient(config, memory)
await client.start()
url = "http://ha.local:8123/api/conversation/process"
with aioresponses() as mocked:
mocked.post(
url,
payload={
"continue_conversation": False,
"conversation_id": "cid-2",
"response": {
"response_type": "query_answer",
"speech": {"plain": {"speech": "在客厅"}},
},
},
status=200,
)
await client.process_text(
"灯开了吗",
prefix_prompt="当前用户对话所在区域:客厅,如后续对话未明确指定区域,则默认为此区域",
)
request = mocked.requests[("POST", url)][0]
payload = json.loads(request.kwargs["data"].decode("utf-8"))
assert payload["text"].startswith("当前用户对话所在区域:客厅")
assert "用户请求:灯开了吗" in payload["text"]
await client.close()
@pytest.mark.asyncio
async def test_reset_conversation(tmp_path):
state_path = tmp_path / "state.json"
memory = ConversationMemory(str(state_path))
await memory.load()
memory.conversation_id = "cid-old"
await memory.save()
config = HomeAssistantConfig(url="http://ha.local:8123", access_token="token")
client = HomeAssistantClient(config, memory)
await client.start()
await client.reset_conversation()
assert memory.conversation_id is None
saved = json.loads(state_path.read_text(encoding="utf-8"))
assert saved["conversation_id"] is None
await client.close()