xiaozhi-sdk 0.0.7__tar.gz → 0.0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {xiaozhi_sdk-0.0.7/xiaozhi_sdk.egg-info → xiaozhi_sdk-0.0.9}/PKG-INFO +1 -1
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/tests/test_xiaozhi.py +13 -16
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/__init__.py +1 -1
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/cli.py +14 -10
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/mcp.py +4 -3
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/utils/mcp_data.py +5 -2
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/utils/mcp_tool.py +6 -2
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9/xiaozhi_sdk.egg-info}/PKG-INFO +1 -1
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/LICENSE +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/MANIFEST.in +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/README.md +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/audio/greet.wav +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/audio/play_music.wav +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/audio/say_hello.wav +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/audio/take_photo.wav +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/image/leijun.jpg +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/opus/linux-arm64-libopus.so +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/opus/linux-x64-libopus.so +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/opus/macos-arm64-libopus.dylib +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/opus/macos-x64-libopus.dylib +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/file/opus/windows-opus.dll +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/pyproject.toml +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/setup.cfg +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/tests/test_iot.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/tests/test_pic.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/__main__.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/config.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/core.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/iot.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/opus.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk/utils/__init__.py +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk.egg-info/SOURCES.txt +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk.egg-info/dependency_links.txt +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk.egg-info/requires.txt +0 -0
- {xiaozhi_sdk-0.0.7 → xiaozhi_sdk-0.0.9}/xiaozhi_sdk.egg-info/top_level.txt +0 -0
|
@@ -69,9 +69,6 @@ async def message_handler_callback(message):
|
|
|
69
69
|
|
|
70
70
|
MAC_ADDR = "00:22:44:66:88:00"
|
|
71
71
|
|
|
72
|
-
ota_url = "http://localhost:3080/api/ota"
|
|
73
|
-
URL = "ws://120.79.156.134:8380"
|
|
74
|
-
|
|
75
72
|
ota_url = None
|
|
76
73
|
URL = None
|
|
77
74
|
|
|
@@ -86,21 +83,21 @@ async def test_main():
|
|
|
86
83
|
await xiaozhi.init_connection(MAC_ADDR)
|
|
87
84
|
|
|
88
85
|
# # say hellow
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# # say take photo
|
|
95
|
-
# for pcm in read_audio_file("./file/audio/take_photo.wav"):
|
|
96
|
-
# await xiaozhi.send_audio(pcm)
|
|
97
|
-
# await xiaozhi.send_silence_audio()
|
|
98
|
-
# await assistant_audio_play(xiaozhi.output_audio_queue, 5)
|
|
86
|
+
for pcm in read_audio_file("./file/audio/say_hello.wav"):
|
|
87
|
+
await xiaozhi.send_audio(pcm)
|
|
88
|
+
await xiaozhi.send_silence_audio()
|
|
89
|
+
await assistant_audio_play(xiaozhi.output_audio_queue)
|
|
99
90
|
|
|
100
|
-
#
|
|
101
|
-
for pcm in read_audio_file("./file/audio/
|
|
91
|
+
# say take photo
|
|
92
|
+
for pcm in read_audio_file("./file/audio/take_photo.wav"):
|
|
102
93
|
await xiaozhi.send_audio(pcm)
|
|
103
94
|
await xiaozhi.send_silence_audio()
|
|
104
|
-
await assistant_audio_play(xiaozhi.output_audio_queue,
|
|
95
|
+
await assistant_audio_play(xiaozhi.output_audio_queue, 5)
|
|
96
|
+
|
|
97
|
+
# play music
|
|
98
|
+
# for pcm in read_audio_file("./file/audio/play_music.wav"):
|
|
99
|
+
# await xiaozhi.send_audio(pcm)
|
|
100
|
+
# await xiaozhi.send_silence_audio()
|
|
101
|
+
# await assistant_audio_play(xiaozhi.output_audio_queue, 500)
|
|
105
102
|
|
|
106
103
|
await xiaozhi.close()
|
|
@@ -46,12 +46,14 @@ async def handle_message(message):
|
|
|
46
46
|
is_end = True
|
|
47
47
|
|
|
48
48
|
|
|
49
|
-
async def play_assistant_audio(audio_queue: deque[bytes]):
|
|
49
|
+
async def play_assistant_audio(audio_queue: deque[bytes], enable_audio):
|
|
50
50
|
"""播放音频流"""
|
|
51
51
|
global is_playing_audio
|
|
52
52
|
|
|
53
|
-
stream =
|
|
54
|
-
|
|
53
|
+
stream = None
|
|
54
|
+
if enable_audio:
|
|
55
|
+
stream = sd.OutputStream(samplerate=INPUT_SERVER_AUDIO_SAMPLE_RATE, channels=1, dtype=np.int16)
|
|
56
|
+
stream.start()
|
|
55
57
|
last_audio_time = None
|
|
56
58
|
|
|
57
59
|
while True:
|
|
@@ -66,7 +68,8 @@ async def play_assistant_audio(audio_queue: deque[bytes]):
|
|
|
66
68
|
|
|
67
69
|
is_playing_audio = True
|
|
68
70
|
pcm_data = audio_queue.popleft()
|
|
69
|
-
stream
|
|
71
|
+
if stream:
|
|
72
|
+
stream.write(pcm_data)
|
|
70
73
|
last_audio_time = time.time()
|
|
71
74
|
|
|
72
75
|
|
|
@@ -83,7 +86,7 @@ class XiaoZhiClient:
|
|
|
83
86
|
self.ota_url = ota_url
|
|
84
87
|
self.mac_address = ""
|
|
85
88
|
|
|
86
|
-
async def start(self, mac_address: str, serial_number: str
|
|
89
|
+
async def start(self, mac_address: str, serial_number: str, license_key: str, enable_audio):
|
|
87
90
|
"""启动客户端连接"""
|
|
88
91
|
self.mac_address = mac_address
|
|
89
92
|
self.xiaozhi = XiaoZhiWebsocket(handle_message, url=self.url, ota_url=self.ota_url, send_wake=True)
|
|
@@ -92,7 +95,7 @@ class XiaoZhiClient:
|
|
|
92
95
|
self.mac_address, aec=False, serial_number=serial_number, license_key=license_key
|
|
93
96
|
)
|
|
94
97
|
|
|
95
|
-
asyncio.create_task(play_assistant_audio(self.xiaozhi.output_audio_queue))
|
|
98
|
+
asyncio.create_task(play_assistant_audio(self.xiaozhi.output_audio_queue, enable_audio))
|
|
96
99
|
|
|
97
100
|
def audio_callback(self, indata, frames, time, status):
|
|
98
101
|
"""音频输入回调函数"""
|
|
@@ -115,11 +118,11 @@ class XiaoZhiClient:
|
|
|
115
118
|
await self.xiaozhi.send_audio(pcm_data)
|
|
116
119
|
|
|
117
120
|
|
|
118
|
-
async def run_client(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str):
|
|
121
|
+
async def run_client(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str, enable_audio: bool):
|
|
119
122
|
"""运行客户端的异步函数"""
|
|
120
123
|
logger.debug("Recording... Press Ctrl+C to stop.")
|
|
121
124
|
client = XiaoZhiClient(url, ota_url)
|
|
122
|
-
await client.start(mac_address, serial_number, license_key)
|
|
125
|
+
await client.start(mac_address, serial_number, license_key, enable_audio)
|
|
123
126
|
|
|
124
127
|
with sd.InputStream(callback=client.audio_callback, channels=1, samplerate=16000, blocksize=960):
|
|
125
128
|
await client.process_audio_input()
|
|
@@ -131,9 +134,10 @@ async def run_client(mac_address: str, url: str, ota_url: str, serial_number: st
|
|
|
131
134
|
@click.option("--ota_url", help="OTA地址")
|
|
132
135
|
@click.option("--serial_number", default="", help="设备的序列号")
|
|
133
136
|
@click.option("--license_key", default="", help="设备的授权密钥")
|
|
134
|
-
|
|
137
|
+
@click.option("--enable_audio", default=True, help="是否开启音频播放")
|
|
138
|
+
def main(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str, enable_audio: bool):
|
|
135
139
|
"""小智SDK客户端
|
|
136
140
|
|
|
137
141
|
MAC_ADDRESS: 设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)
|
|
138
142
|
"""
|
|
139
|
-
asyncio.run(run_client(mac_address, url, ota_url, serial_number, license_key))
|
|
143
|
+
asyncio.run(run_client(mac_address, url, ota_url, serial_number, license_key, enable_audio))
|
|
@@ -43,7 +43,7 @@ class McpTool(object):
|
|
|
43
43
|
try:
|
|
44
44
|
response = requests.post(self.explain_url, files=files, data=payload, headers=headers, timeout=5)
|
|
45
45
|
res_json = response.json()
|
|
46
|
-
except Exception
|
|
46
|
+
except Exception:
|
|
47
47
|
return "网络异常", True
|
|
48
48
|
if res_json.get("error"):
|
|
49
49
|
return res_json, True
|
|
@@ -72,10 +72,11 @@ class McpTool(object):
|
|
|
72
72
|
else:
|
|
73
73
|
tool_res, is_error = {"message": "正在为你播放: {}".format(arguments["music_name"])}, False
|
|
74
74
|
data = {
|
|
75
|
-
"type": "music",
|
|
75
|
+
"type": "music",
|
|
76
|
+
"state": "start",
|
|
76
77
|
"url": music_info["url"],
|
|
77
78
|
"text": arguments["music_name"],
|
|
78
|
-
"source": "sdk.mcp_music_tool"
|
|
79
|
+
"source": "sdk.mcp_music_tool",
|
|
79
80
|
}
|
|
80
81
|
await self.message_handler_callback(data)
|
|
81
82
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
1
|
+
from typing import Any, Dict
|
|
2
2
|
|
|
3
3
|
mcp_initialize_payload: Dict[str, Any] = {
|
|
4
4
|
"jsonrpc": "2.0",
|
|
@@ -23,7 +23,10 @@ mcp_tool_conf: Dict[str, Dict[str, Any]] = {
|
|
|
23
23
|
"description": "Play music using music IDs. IMPORTANT: You must call `search_custom_music` first to get the music IDs before using this tool. Use this tool after getting music IDs from search results. Args:\n `id_list`: The id list of the music to play (obtained from search_custom_music results). The list must contain more than 2 music IDs, and the system will randomly select one to play.\n `music_name`: The name of the music (obtained from search_custom_music results)",
|
|
24
24
|
"inputSchema": {
|
|
25
25
|
"type": "object",
|
|
26
|
-
"properties": {
|
|
26
|
+
"properties": {
|
|
27
|
+
"music_name": {"type": "string"},
|
|
28
|
+
"id_list": {"type": "array", "items": {"type": "string"}, "minItems": 3},
|
|
29
|
+
},
|
|
27
30
|
"required": ["music_name", "id_list"],
|
|
28
31
|
},
|
|
29
32
|
},
|
|
@@ -3,7 +3,6 @@ import random
|
|
|
3
3
|
|
|
4
4
|
import aiohttp
|
|
5
5
|
import numpy as np
|
|
6
|
-
from pydub import AudioSegment
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
async def async_search_custom_music(data) -> tuple[dict, bool]:
|
|
@@ -57,6 +56,11 @@ async def _get_random_music_info(id_list: list) -> dict:
|
|
|
57
56
|
|
|
58
57
|
|
|
59
58
|
async def async_mcp_play_music(data) -> tuple[list, bool]:
|
|
59
|
+
try:
|
|
60
|
+
from pydub import AudioSegment
|
|
61
|
+
except ImportError:
|
|
62
|
+
return [], True
|
|
63
|
+
|
|
60
64
|
id_list = data["id_list"]
|
|
61
65
|
res_json = await _get_random_music_info(id_list)
|
|
62
66
|
|
|
@@ -79,7 +83,7 @@ async def async_mcp_play_music(data) -> tuple[list, bool]:
|
|
|
79
83
|
|
|
80
84
|
chunk_size = 960 * 2
|
|
81
85
|
for i in range(0, len(pcm_data), chunk_size):
|
|
82
|
-
chunk = pcm_data[i: i + chunk_size]
|
|
86
|
+
chunk = pcm_data[i : i + chunk_size]
|
|
83
87
|
|
|
84
88
|
if chunk: # 确保不添加空块
|
|
85
89
|
chunk = np.frombuffer(chunk, dtype=np.int16)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|