xiaozhi-sdk 0.0.8__tar.gz → 0.0.10__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.

Potentially problematic release.


This version of xiaozhi-sdk might be problematic. Click here for more details.

Files changed (35) hide show
  1. {xiaozhi_sdk-0.0.8/xiaozhi_sdk.egg-info → xiaozhi_sdk-0.0.10}/PKG-INFO +1 -1
  2. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/tests/test_xiaozhi.py +13 -16
  3. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/__init__.py +1 -1
  4. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/cli.py +56 -13
  5. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/iot.py +4 -1
  6. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/mcp.py +4 -3
  7. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/utils/mcp_data.py +5 -2
  8. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/utils/mcp_tool.py +1 -1
  9. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10/xiaozhi_sdk.egg-info}/PKG-INFO +1 -1
  10. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/LICENSE +0 -0
  11. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/MANIFEST.in +0 -0
  12. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/README.md +0 -0
  13. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/audio/greet.wav +0 -0
  14. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/audio/play_music.wav +0 -0
  15. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/audio/say_hello.wav +0 -0
  16. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/audio/take_photo.wav +0 -0
  17. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/image/leijun.jpg +0 -0
  18. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/opus/linux-arm64-libopus.so +0 -0
  19. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/opus/linux-x64-libopus.so +0 -0
  20. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/opus/macos-arm64-libopus.dylib +0 -0
  21. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/opus/macos-x64-libopus.dylib +0 -0
  22. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/file/opus/windows-opus.dll +0 -0
  23. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/pyproject.toml +0 -0
  24. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/setup.cfg +0 -0
  25. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/tests/test_iot.py +0 -0
  26. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/tests/test_pic.py +0 -0
  27. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/__main__.py +0 -0
  28. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/config.py +0 -0
  29. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/core.py +0 -0
  30. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/opus.py +0 -0
  31. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk/utils/__init__.py +0 -0
  32. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk.egg-info/SOURCES.txt +0 -0
  33. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk.egg-info/dependency_links.txt +0 -0
  34. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk.egg-info/requires.txt +0 -0
  35. {xiaozhi_sdk-0.0.8 → xiaozhi_sdk-0.0.10}/xiaozhi_sdk.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xiaozhi-sdk
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
5
5
  Author-email: dairoot <623815825@qq.com>
6
6
  License-Expression: MIT
@@ -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
- # for pcm in read_audio_file("./file/audio/say_hello.wav"):
90
- # await xiaozhi.send_audio(pcm)
91
- # await xiaozhi.send_silence_audio()
92
- # await assistant_audio_play(xiaozhi.output_audio_queue)
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
- # play music
101
- for pcm in read_audio_file("./file/audio/play_music.wav"):
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, 500)
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()
@@ -1,3 +1,3 @@
1
- __version__ = "0.0.8"
1
+ __version__ = "0.0.10"
2
2
 
3
3
  from xiaozhi_sdk.core import XiaoZhiWebsocket # noqa
@@ -12,6 +12,34 @@ import sounddevice as sd
12
12
  from xiaozhi_sdk import XiaoZhiWebsocket
13
13
  from xiaozhi_sdk.config import INPUT_SERVER_AUDIO_SAMPLE_RATE
14
14
 
15
+ # 定义自定义日志级别
16
+ INFO1 = 21
17
+ INFO2 = 22
18
+ INFO3 = 23
19
+
20
+ # 添加自定义日志级别到logging模块
21
+ logging.addLevelName(INFO1, "INFO1")
22
+ logging.addLevelName(INFO2, "INFO2")
23
+ logging.addLevelName(INFO3, "INFO3")
24
+
25
+ # 为logger添加自定义方法
26
+ def info1(self, message, *args, **kwargs):
27
+ if self.isEnabledFor(INFO1):
28
+ self._log(INFO1, message, args, **kwargs)
29
+
30
+ def info2(self, message, *args, **kwargs):
31
+ if self.isEnabledFor(INFO2):
32
+ self._log(INFO2, message, args, **kwargs)
33
+
34
+ def info3(self, message, *args, **kwargs):
35
+ if self.isEnabledFor(INFO3):
36
+ self._log(INFO3, message, args, **kwargs)
37
+
38
+ # 将自定义方法添加到Logger类
39
+ logging.Logger.info1 = info1
40
+ logging.Logger.info2 = info2
41
+ logging.Logger.info3 = info3
42
+
15
43
  # 配置彩色logging
16
44
  handler = colorlog.StreamHandler()
17
45
  handler.setFormatter(
@@ -19,8 +47,11 @@ handler.setFormatter(
19
47
  "%(log_color)s%(asctime)s - %(name)s - %(levelname)s - %(message)s",
20
48
  datefmt="%Y-%m-%d %H:%M:%S",
21
49
  log_colors={
22
- "DEBUG": "green",
23
- "INFO": "white",
50
+ "DEBUG": "white",
51
+ "INFO": "green",
52
+ "INFO1": "green",
53
+ "INFO2": "cyan",
54
+ "INFO3": "blue",
24
55
  "WARNING": "yellow",
25
56
  "ERROR": "red",
26
57
  "CRITICAL": "red,bg_white",
@@ -41,17 +72,27 @@ is_end = False
41
72
  async def handle_message(message):
42
73
  """处理接收到的消息"""
43
74
  global is_end
44
- logger.info("message received: %s", message)
75
+ if message["type"] == "stt":
76
+ logger.info1("message received: %s", message)
77
+ elif message["type"] == "tts":
78
+ logger.info2("message received: %s", message)
79
+ elif message["type"] == "llm":
80
+ logger.info3("message received: %s", message)
81
+ else:
82
+ logger.info("message received: %s", message)
83
+
45
84
  if message["type"] == "websocket" and message["state"] == "close":
46
85
  is_end = True
47
86
 
48
87
 
49
- async def play_assistant_audio(audio_queue: deque[bytes]):
88
+ async def play_assistant_audio(audio_queue: deque[bytes], enable_audio):
50
89
  """播放音频流"""
51
90
  global is_playing_audio
52
91
 
53
- stream = sd.OutputStream(samplerate=INPUT_SERVER_AUDIO_SAMPLE_RATE, channels=1, dtype=np.int16)
54
- stream.start()
92
+ stream = None
93
+ if enable_audio:
94
+ stream = sd.OutputStream(samplerate=INPUT_SERVER_AUDIO_SAMPLE_RATE, channels=1, dtype=np.int16)
95
+ stream.start()
55
96
  last_audio_time = None
56
97
 
57
98
  while True:
@@ -66,7 +107,8 @@ async def play_assistant_audio(audio_queue: deque[bytes]):
66
107
 
67
108
  is_playing_audio = True
68
109
  pcm_data = audio_queue.popleft()
69
- stream.write(pcm_data)
110
+ if stream:
111
+ stream.write(pcm_data)
70
112
  last_audio_time = time.time()
71
113
 
72
114
 
@@ -83,7 +125,7 @@ class XiaoZhiClient:
83
125
  self.ota_url = ota_url
84
126
  self.mac_address = ""
85
127
 
86
- async def start(self, mac_address: str, serial_number: str = "", license_key: str = ""):
128
+ async def start(self, mac_address: str, serial_number: str, license_key: str, enable_audio):
87
129
  """启动客户端连接"""
88
130
  self.mac_address = mac_address
89
131
  self.xiaozhi = XiaoZhiWebsocket(handle_message, url=self.url, ota_url=self.ota_url, send_wake=True)
@@ -92,7 +134,7 @@ class XiaoZhiClient:
92
134
  self.mac_address, aec=False, serial_number=serial_number, license_key=license_key
93
135
  )
94
136
 
95
- asyncio.create_task(play_assistant_audio(self.xiaozhi.output_audio_queue))
137
+ asyncio.create_task(play_assistant_audio(self.xiaozhi.output_audio_queue, enable_audio))
96
138
 
97
139
  def audio_callback(self, indata, frames, time, status):
98
140
  """音频输入回调函数"""
@@ -115,11 +157,11 @@ class XiaoZhiClient:
115
157
  await self.xiaozhi.send_audio(pcm_data)
116
158
 
117
159
 
118
- async def run_client(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str):
160
+ async def run_client(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str, enable_audio: bool):
119
161
  """运行客户端的异步函数"""
120
162
  logger.debug("Recording... Press Ctrl+C to stop.")
121
163
  client = XiaoZhiClient(url, ota_url)
122
- await client.start(mac_address, serial_number, license_key)
164
+ await client.start(mac_address, serial_number, license_key, enable_audio)
123
165
 
124
166
  with sd.InputStream(callback=client.audio_callback, channels=1, samplerate=16000, blocksize=960):
125
167
  await client.process_audio_input()
@@ -131,9 +173,10 @@ async def run_client(mac_address: str, url: str, ota_url: str, serial_number: st
131
173
  @click.option("--ota_url", help="OTA地址")
132
174
  @click.option("--serial_number", default="", help="设备的序列号")
133
175
  @click.option("--license_key", default="", help="设备的授权密钥")
134
- def main(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str):
176
+ @click.option("--enable_audio", default=True, help="是否开启音频播放")
177
+ def main(mac_address: str, url: str, ota_url: str, serial_number: str, license_key: str, enable_audio: bool):
135
178
  """小智SDK客户端
136
179
 
137
180
  MAC_ADDRESS: 设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)
138
181
  """
139
- asyncio.run(run_client(mac_address, url, ota_url, serial_number, license_key))
182
+ asyncio.run(run_client(mac_address, url, ota_url, serial_number, license_key, enable_audio))
@@ -12,7 +12,7 @@ from xiaozhi_sdk.config import OTA_URL
12
12
  # 常量定义
13
13
  BOARD_TYPE = "xiaozhi-sdk-box"
14
14
  USER_AGENT = "xiaozhi-sdk/{}".format(__version__)
15
- BOARD_NAME = "xiaozhi-sdk-{}".format(__version__)
15
+ BOARD_NAME = "xiaozhi-sdk"
16
16
 
17
17
  logger = logging.getLogger("xiaozhi_sdk")
18
18
 
@@ -60,6 +60,9 @@ class OtaDevice:
60
60
 
61
61
  async with aiohttp.ClientSession() as session:
62
62
  async with session.post(self.ota_url + "/", headers=headers, data=json.dumps(payload)) as response:
63
+ if response.status != 200:
64
+ err_text = await response.text()
65
+ raise Exception(err_text)
63
66
  response.raise_for_status()
64
67
  return await response.json()
65
68
 
@@ -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 as e:
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", "state": "start",
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, List
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": {"music_name": {"type": "string"}, "id_list": {"type": "array", "items": {"type": "string"}, "minItems": 3}},
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
  },
@@ -83,7 +83,7 @@ async def async_mcp_play_music(data) -> tuple[list, bool]:
83
83
 
84
84
  chunk_size = 960 * 2
85
85
  for i in range(0, len(pcm_data), chunk_size):
86
- chunk = pcm_data[i: i + chunk_size]
86
+ chunk = pcm_data[i : i + chunk_size]
87
87
 
88
88
  if chunk: # 确保不添加空块
89
89
  chunk = np.frombuffer(chunk, dtype=np.int16)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: xiaozhi-sdk
3
- Version: 0.0.8
3
+ Version: 0.0.10
4
4
  Summary: 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
5
5
  Author-email: dairoot <623815825@qq.com>
6
6
  License-Expression: MIT
File without changes
File without changes
File without changes
File without changes
File without changes