xiaozhi-sdk 0.1.0__tar.gz → 1.0.0__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 (25) hide show
  1. xiaozhi_sdk-1.0.0/LICENSE +21 -0
  2. xiaozhi_sdk-1.0.0/PKG-INFO +85 -0
  3. xiaozhi_sdk-1.0.0/README.md +53 -0
  4. xiaozhi_sdk-1.0.0/setup.cfg +11 -0
  5. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/setup.py +11 -6
  6. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/tests/test_pic.py +3 -2
  7. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/tests/test_xiaozhi.py +24 -30
  8. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/__init__.py +22 -22
  9. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/__main__.py +15 -11
  10. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/data.py +5 -3
  11. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/iot.py +6 -4
  12. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/mcp.py +13 -11
  13. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/opus.py +1 -0
  14. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/utils.py +4 -3
  15. xiaozhi_sdk-1.0.0/xiaozhi_sdk.egg-info/PKG-INFO +85 -0
  16. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk.egg-info/SOURCES.txt +2 -0
  17. xiaozhi_sdk-1.0.0/xiaozhi_sdk.egg-info/requires.txt +8 -0
  18. xiaozhi_sdk-0.1.0/PKG-INFO +0 -58
  19. xiaozhi_sdk-0.1.0/README.md +0 -33
  20. xiaozhi_sdk-0.1.0/setup.cfg +0 -4
  21. xiaozhi_sdk-0.1.0/xiaozhi_sdk.egg-info/PKG-INFO +0 -58
  22. xiaozhi_sdk-0.1.0/xiaozhi_sdk.egg-info/requires.txt +0 -3
  23. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk/config.py +0 -0
  24. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk.egg-info/dependency_links.txt +0 -0
  25. {xiaozhi_sdk-0.1.0 → xiaozhi_sdk-1.0.0}/xiaozhi_sdk.egg-info/top_level.txt +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 dairoot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.4
2
+ Name: xiaozhi-sdk
3
+ Version: 1.0.0
4
+ Summary: 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
5
+ Home-page: https://github.com/dairoot/xiaozhi-sdk
6
+ Author: dairoot
7
+ Author-email: 623815825@qq.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: numpy
15
+ Requires-Dist: websockets
16
+ Requires-Dist: aiohttp
17
+ Requires-Dist: av
18
+ Requires-Dist: opuslib
19
+ Requires-Dist: requests
20
+ Requires-Dist: sounddevice
21
+ Requires-Dist: python-socks
22
+ Dynamic: author
23
+ Dynamic: author-email
24
+ Dynamic: classifier
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: license-file
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # 小智SDK (XiaoZhi SDK)
34
+
35
+ [![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
36
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
37
+ [![PyPI](https://img.shields.io/badge/pypi-xiaozhi--sdk-blue.svg)](https://pypi.org/project/xiaozhi-sdk/)
38
+
39
+ 一个用于连接和控制小智服务的 Python SDK,支持实时音频通信、MCP 工具集成和设备管理等功能。
40
+
41
+ ---
42
+
43
+ ## 📦 安装
44
+
45
+ ```bash
46
+ pip install xiaozhi-sdk
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 🚀 快速开始
52
+
53
+ ### 1. 终端使用
54
+
55
+ 最简单的方式是通过命令行连接设备:
56
+
57
+ #### 查看帮助信息
58
+
59
+ ```bash
60
+ python -m xiaozhi_sdk -h
61
+ ```
62
+
63
+ 输出示例:
64
+
65
+ ```text
66
+ positional arguments:
67
+ device 你的小智设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)
68
+
69
+ options:
70
+ -h, --help 显示帮助信息并退出
71
+ --url URL 小智服务 websocket 地址
72
+ --ota_url OTA_URL 小智 OTA 地址
73
+ ```
74
+
75
+ #### 连接设备(需要提供 MAC 地址)
76
+
77
+ ```bash
78
+ python -m xiaozhi_sdk 00:11:22:33:44:55
79
+ ```
80
+
81
+ ### 2. 编程使用
82
+ 参考 [examples](examples/) 文件中的示例代码,可以快速开始使用 SDK。
83
+
84
+
85
+
@@ -0,0 +1,53 @@
1
+ # 小智SDK (XiaoZhi SDK)
2
+
3
+ [![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
4
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
+ [![PyPI](https://img.shields.io/badge/pypi-xiaozhi--sdk-blue.svg)](https://pypi.org/project/xiaozhi-sdk/)
6
+
7
+ 一个用于连接和控制小智服务的 Python SDK,支持实时音频通信、MCP 工具集成和设备管理等功能。
8
+
9
+ ---
10
+
11
+ ## 📦 安装
12
+
13
+ ```bash
14
+ pip install xiaozhi-sdk
15
+ ```
16
+
17
+ ---
18
+
19
+ ## 🚀 快速开始
20
+
21
+ ### 1. 终端使用
22
+
23
+ 最简单的方式是通过命令行连接设备:
24
+
25
+ #### 查看帮助信息
26
+
27
+ ```bash
28
+ python -m xiaozhi_sdk -h
29
+ ```
30
+
31
+ 输出示例:
32
+
33
+ ```text
34
+ positional arguments:
35
+ device 你的小智设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)
36
+
37
+ options:
38
+ -h, --help 显示帮助信息并退出
39
+ --url URL 小智服务 websocket 地址
40
+ --ota_url OTA_URL 小智 OTA 地址
41
+ ```
42
+
43
+ #### 连接设备(需要提供 MAC 地址)
44
+
45
+ ```bash
46
+ python -m xiaozhi_sdk 00:11:22:33:44:55
47
+ ```
48
+
49
+ ### 2. 编程使用
50
+ 参考 [examples](examples/) 文件中的示例代码,可以快速开始使用 SDK。
51
+
52
+
53
+
@@ -0,0 +1,11 @@
1
+ [flake8]
2
+ ignore = E203, E266, E501, W503, F403, F401, E731, E711, C901, F841, D103, B008, D400, E402, E712
3
+ max-line-length = 120
4
+ max-complexity = 20
5
+ exclude =
6
+ tests/*
7
+
8
+ [egg_info]
9
+ tag_build =
10
+ tag_date = 0
11
+
@@ -2,18 +2,23 @@ from setuptools import find_packages, setup
2
2
 
3
3
  setup(
4
4
  name="xiaozhi-sdk", # 包名
5
- version="0.1.0", # 版本号
5
+ version="1.0.0", # 版本号
6
6
  packages=find_packages(), # 自动发现包
7
7
  install_requires=[ # 依赖
8
8
  "numpy",
9
- "requests>=2.32.1",
10
- "sounddevice>=0.4.2",
9
+ "websockets",
10
+ "aiohttp",
11
+ "av",
12
+ "opuslib",
13
+ "requests",
14
+ "sounddevice",
15
+ "python-socks"
11
16
  ],
12
17
  author="dairoot",
13
18
  author_email="623815825@qq.com", # 作者邮箱
14
- description="A short description of your package", # 简短描述
15
- long_description=open('README.md').read(), # 详细描述(通常从 README 读取)
16
- long_description_content_type='text/markdown', # README 文件格式
19
+ description="一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。", # 简短描述
20
+ long_description=open("README.md").read(), # 详细描述(通常从 README 读取)
21
+ long_description_content_type="text/markdown", # README 文件格式
17
22
  url="https://github.com/dairoot/xiaozhi-sdk", # 项目主页
18
23
  classifiers=[ # 分类元数据
19
24
  "Programming Language :: Python :: 3",
@@ -1,6 +1,7 @@
1
- import aiohttp
2
1
  import asyncio
3
2
 
3
+ import aiohttp
4
+
4
5
 
5
6
  async def explain_image():
6
7
  url = "http://api.xiaozhi.me/mcp/vision/explain"
@@ -9,7 +10,6 @@ async def explain_image():
9
10
 
10
11
  boundary = "----ESP32_CAMERA_BOUNDARY"
11
12
  headers = {
12
-
13
13
  "Content-Type": f"multipart/form-data; boundary={boundary}",
14
14
  # 不设置Transfer-Encoding,aiohttp自动处理
15
15
  }
@@ -42,5 +42,6 @@ async def explain_image():
42
42
  text = await resp.text()
43
43
  print("Response:", text)
44
44
 
45
+
45
46
  if __name__ == "__main__":
46
47
  asyncio.run(explain_image())
@@ -1,11 +1,10 @@
1
1
  import asyncio
2
- import json
3
2
  import os
4
3
  import sys
5
4
  import time
6
- import wave
7
5
 
8
6
  import numpy as np
7
+ import pytest
9
8
  import sounddevice as sd
10
9
 
11
10
  sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
@@ -13,8 +12,6 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
13
12
  from xiaozhi_sdk import XiaoZhiWebsocket
14
13
  from xiaozhi_sdk.utils import read_audio_file
15
14
 
16
- play_audio_over = asyncio.Event()
17
-
18
15
 
19
16
  async def assistant_audio_play(audio_queue):
20
17
  # 创建一个持续播放的流
@@ -24,7 +21,7 @@ async def assistant_audio_play(audio_queue):
24
21
  while True:
25
22
  if not audio_queue:
26
23
  await asyncio.sleep(0.01)
27
- if last_time and time.time() - last_time > 3:
24
+ if last_time and time.time() - last_time > 1:
28
25
  break
29
26
 
30
27
  continue
@@ -33,22 +30,20 @@ async def assistant_audio_play(audio_queue):
33
30
  stream.write(pcm_data)
34
31
  last_time = time.time()
35
32
 
36
- play_audio_over.set()
37
33
  stream.stop()
38
34
  stream.close()
39
35
 
40
36
 
41
37
  def mcp_tool_func():
42
-
43
38
  def mcp_take_photo(data):
44
39
  return open("./file/leijun.jpg", "rb")
45
40
 
46
41
  def mcp_get_device_status(data):
47
42
  return {
48
- "audio_speaker": {"volume": 80},
49
- "screen": {"brightness": 75, "theme": "light"},
50
- "network": {"type": "wifi", "ssid": "wifi名称", "signal": "strong"},
51
- }
43
+ "audio_speaker": {"volume": 80},
44
+ "screen": {"brightness": 75, "theme": "light"},
45
+ "network": {"type": "wifi", "ssid": "wifi名称", "signal": "strong"},
46
+ }
52
47
 
53
48
  def mcp_set_volume(data):
54
49
  return {}
@@ -61,32 +56,31 @@ def mcp_tool_func():
61
56
  return mcp_tool_func
62
57
 
63
58
 
59
+ async def message_handler_callback(message):
60
+ print("message received:", message)
64
61
 
65
62
 
63
+ MAC_ADDR = "fc:01:2c:c9:2b:31"
64
+ # URL = "ws://120.79.156.134:8380"
65
+ URL = None
66
66
 
67
- async def main():
68
- async def message_handler_callback(message):
69
- print("message received:", message)
70
67
 
71
- MAC_ADDR = "fc:01:2c:c9:2b:31"
72
- url = "ws://120.79.156.134:8380"
73
- url = None
74
- xiaozhi = XiaoZhiWebsocket(message_handler_callback, url=url)
68
+ @pytest.mark.asyncio
69
+ async def test_main():
70
+ xiaozhi = XiaoZhiWebsocket(message_handler_callback, url=URL)
75
71
  await xiaozhi.set_mcp_tool_callback(mcp_tool_func())
76
72
  await xiaozhi.init_connection(MAC_ADDR)
77
- asyncio.create_task(assistant_audio_play(xiaozhi.audio_queue))
78
- await asyncio.sleep(1)
79
-
80
- # 使用新的音频读取函数
81
- for pcm in read_audio_file("./file/take_photo.wav"):
82
- # for pcm in read_audio_file("./file/say_hello.wav"):
83
- await xiaozhi.send_audio(pcm)
84
73
 
85
- # 发送静音数据
74
+ # say hellow
75
+ for pcm in read_audio_file("./file/say_hello.wav"):
76
+ await xiaozhi.send_audio(pcm)
86
77
  await xiaozhi.send_silence_audio()
87
- await asyncio.wait_for(play_audio_over.wait(), timeout=20.0)
88
- await xiaozhi.close()
78
+ await assistant_audio_play(xiaozhi.audio_queue)
89
79
 
80
+ # say take photo
81
+ for pcm in read_audio_file("./file/take_photo.wav"):
82
+ await xiaozhi.send_audio(pcm)
83
+ await xiaozhi.send_silence_audio()
84
+ await assistant_audio_play(xiaozhi.audio_queue)
90
85
 
91
- if __name__ == "__main__":
92
- asyncio.run(main())
86
+ await xiaozhi.close()
@@ -3,22 +3,24 @@ import json
3
3
  import os
4
4
  import uuid
5
5
  import wave
6
- import websockets
7
6
  from collections import deque
8
- from typing import Dict
7
+ from typing import Any, Callable, Dict
8
+
9
+ import websockets
9
10
 
10
11
  from xiaozhi_sdk.config import INPUT_SERVER_AUDIO_SAMPLE_RATE, WSS_URL
11
12
  from xiaozhi_sdk.iot import OtaDevice
12
13
  from xiaozhi_sdk.mcp import McpTool
13
14
  from xiaozhi_sdk.opus import AudioOpus
14
- from xiaozhi_sdk.utils import read_audio_file, get_wav_info
15
+ from xiaozhi_sdk.utils import get_wav_info, read_audio_file
15
16
 
16
17
 
17
18
  class XiaoZhiWebsocket(McpTool):
18
19
 
19
- def __init__(self, message_handler_callback=None, url=None, audio_sample_rate=16000, audio_channels=1):
20
+ def __init__(self, message_handler_callback=None, url=None, ota_url=None, audio_sample_rate=16000, audio_channels=1):
20
21
  super().__init__()
21
22
  self.url = url or WSS_URL
23
+ self.ota_url = ota_url
22
24
  self.audio_sample_rate = audio_sample_rate
23
25
  self.audio_channels = audio_channels
24
26
  self.audio_opus = AudioOpus(audio_sample_rate, audio_channels)
@@ -31,6 +33,7 @@ class XiaoZhiWebsocket(McpTool):
31
33
  self.audio_queue = deque()
32
34
  self.websocket = None
33
35
  self.message_handler_task = None
36
+ self.ota = None
34
37
 
35
38
  async def send_hello(self, aec: bool):
36
39
  hello_message = {
@@ -49,32 +52,27 @@ class XiaoZhiWebsocket(McpTool):
49
52
  await asyncio.wait_for(self.hello_received.wait(), timeout=10.0)
50
53
 
51
54
  async def start_listen(self):
52
- listen_message = {
53
- "session_id": self.session_id,
54
- "type": "listen",
55
- "state": "start",
56
- "mode": "realtime"
57
- }
55
+ listen_message = {"session_id": self.session_id, "type": "listen", "state": "start", "mode": "realtime"}
58
56
  await self.websocket.send(json.dumps(listen_message))
59
57
 
60
- async def set_mcp_tool_callback(self, tool_func: Dict[str, callable]):
58
+ async def set_mcp_tool_callback(self, tool_func: Dict[str, Callable[..., Any]]):
61
59
  self.tool_func = tool_func
62
60
 
63
- async def activate_iot_device(self):
64
- ota = OtaDevice(self.mac_addr, self.client_id)
65
-
66
- data = await ota.activate_device()
67
- if data.get("activation"):
68
- await self.send_demo_get_code_audio()
69
- challenge = data["activation"]["challenge"]
61
+ async def activate_iot_device(self, ota_info):
62
+ if ota_info.get("activation"):
63
+ await self.send_demo_audio()
64
+ challenge = ota_info["activation"]["challenge"]
70
65
  await asyncio.sleep(3)
71
66
  for _ in range(10):
72
- if await ota.check_activate(challenge):
67
+ if await self.ota.check_activate(challenge):
73
68
  break
74
69
  await asyncio.sleep(3)
75
70
 
76
71
  async def init_connection(self, mac_addr: str, aec: bool = False):
77
72
  self.mac_addr = mac_addr
73
+ self.ota = OtaDevice(self.mac_addr, self.client_id, self.ota_url)
74
+ ota_info = await self.ota.activate_device()
75
+
78
76
  headers = {
79
77
  "Authorization": "Bearer test-token",
80
78
  "Protocol-Version": "1",
@@ -86,9 +84,9 @@ class XiaoZhiWebsocket(McpTool):
86
84
  self.message_handler_task = asyncio.create_task(self.message_handler())
87
85
  await self.send_hello(aec)
88
86
  await self.start_listen()
89
- asyncio.create_task(self.activate_iot_device())
87
+ asyncio.create_task(self.activate_iot_device(ota_info))
90
88
 
91
- async def send_demo_get_code_audio(self):
89
+ async def send_demo_audio(self):
92
90
  current_dir = os.path.dirname(os.path.abspath(__file__))
93
91
  wav_path = os.path.join(current_dir, "../file/greet.wav")
94
92
  framerate, nchannels = get_wav_info(wav_path)
@@ -97,6 +95,7 @@ class XiaoZhiWebsocket(McpTool):
97
95
  for pcm_data in read_audio_file(wav_path):
98
96
  opus_data = await audio_opus.pcm_to_opus(pcm_data)
99
97
  await self.websocket.send(opus_data)
98
+ await self.send_silence_audio()
100
99
 
101
100
  async def send_silence_audio(self, duration_seconds: float = 1.2):
102
101
  # 发送 静音数据
@@ -141,7 +140,8 @@ class XiaoZhiWebsocket(McpTool):
141
140
  except websockets.ConnectionClosed:
142
141
  if self.message_handler_callback:
143
142
  await self.message_handler_callback(
144
- {"type": "websocket", "state": "close", "source": "sdk.message_handler"})
143
+ {"type": "websocket", "state": "close", "source": "sdk.message_handler"}
144
+ )
145
145
 
146
146
  async def close(self):
147
147
  if self.message_handler_task and not self.message_handler_task.done():
@@ -1,15 +1,15 @@
1
1
  import argparse
2
2
  import asyncio
3
+ import numpy as np
3
4
  import re
5
+ import sounddevice as sd
4
6
  import time
5
7
  from collections import deque
6
8
 
7
- import numpy as np
8
- import sounddevice as sd
9
-
10
9
  from xiaozhi_sdk import XiaoZhiWebsocket
10
+ from xiaozhi_sdk.config import INPUT_SERVER_AUDIO_SAMPLE_RATE
11
11
 
12
- input_audio = deque()
12
+ input_audio: deque[bytes] = deque()
13
13
 
14
14
  is_play_audio = False
15
15
 
@@ -21,7 +21,7 @@ async def message_handler_callback(message):
21
21
  async def assistant_audio_play(audio_queue):
22
22
  global is_play_audio
23
23
  # 创建一个持续播放的流
24
- stream = sd.OutputStream(samplerate=16000, channels=1, dtype=np.int16)
24
+ stream = sd.OutputStream(samplerate=INPUT_SERVER_AUDIO_SAMPLE_RATE, channels=1, dtype=np.int16)
25
25
  stream.start()
26
26
  last_time = None
27
27
 
@@ -40,13 +40,14 @@ async def assistant_audio_play(audio_queue):
40
40
 
41
41
 
42
42
  class Client:
43
- def __init__(self, mac_address):
43
+ def __init__(self, mac_address, url=None, ota_url=None):
44
44
  self.mac_address = mac_address
45
45
  self.xiaozhi = None
46
- pass
46
+ self.url = url
47
+ self.ota_url = ota_url
47
48
 
48
49
  async def start(self):
49
- self.xiaozhi = XiaoZhiWebsocket(message_handler_callback)
50
+ self.xiaozhi = XiaoZhiWebsocket(message_handler_callback, url=self.url, ota_url=self.ota_url)
50
51
  await self.xiaozhi.init_connection(self.mac_address, aec=False)
51
52
  asyncio.create_task(assistant_audio_play(self.xiaozhi.audio_queue))
52
53
 
@@ -66,7 +67,7 @@ class Client:
66
67
 
67
68
  def mac_address(string):
68
69
  """验证是否为有效的MAC地址"""
69
- if re.fullmatch(r'([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}', string):
70
+ if re.fullmatch(r"([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}", string):
70
71
  return string
71
72
  else:
72
73
  raise argparse.ArgumentTypeError(f"无效的MAC地址格式: '{string}'")
@@ -74,10 +75,13 @@ def mac_address(string):
74
75
 
75
76
  async def main():
76
77
  parser = argparse.ArgumentParser(description="这是一个小智SDK。")
77
- parser.add_argument('device', type=mac_address, help='你的小智设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)')
78
+ parser.add_argument("device", type=mac_address, help="你的小智设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)")
79
+ parser.add_argument("--url", help="小智服务 websocket 地址")
80
+ parser.add_argument("--ota_url", help="小智 OTA 地址")
81
+
78
82
 
79
83
  args = parser.parse_args()
80
- client = Client(args.device)
84
+ client = Client(args.device, args.url, args.ota_url)
81
85
  await client.start()
82
86
  await asyncio.sleep(2)
83
87
 
@@ -1,4 +1,6 @@
1
- mcp_initialize_payload = {
1
+ from typing import Any, Dict, List
2
+
3
+ mcp_initialize_payload: Dict[str, Any] = {
2
4
  "jsonrpc": "2.0",
3
5
  "id": 1,
4
6
  "result": {
@@ -8,7 +10,7 @@ mcp_initialize_payload = {
8
10
  },
9
11
  }
10
12
 
11
- mcp_tool_conf = {
13
+ mcp_tool_conf: Dict[str, Dict[str, Any]] = {
12
14
  "get_device_status": {
13
15
  "description": "Provides the real-time information of the device, including the current status of the audio speaker, screen, battery, network, etc.\nUse this tool for: \n1. Answering questions about current condition (e.g. what is the current volume of the audio speaker?)\n2. As the first step to control the device (e.g. turn up / down the volume of the audio speaker, etc.)",
14
16
  "inputSchema": {"type": "object", "properties": {}},
@@ -51,7 +53,7 @@ mcp_tool_conf = {
51
53
  },
52
54
  }
53
55
 
54
- mcp_tools_payload = {
56
+ mcp_tools_payload: Dict[str, Any] = {
55
57
  "jsonrpc": "2.0",
56
58
  "id": 2,
57
59
  "result": {"tools": []},
@@ -1,14 +1,16 @@
1
- import aiohttp
2
1
  import json
3
2
 
3
+ import aiohttp
4
4
  from xiaozhi_sdk.config import OTA_URL
5
5
 
6
+
6
7
  USER_AGENT = "XiaoXhi-SDK/1.0"
7
8
 
8
9
 
9
10
  class OtaDevice(object):
10
11
 
11
- def __init__(self, mac_addr: str, client_id: str, serial_number: str = ""):
12
+ def __init__(self, mac_addr: str, client_id: str, ota_url: str, serial_number: str = ""):
13
+ self.ota_url = ota_url or OTA_URL
12
14
  self.mac_addr = mac_addr
13
15
  self.client_id = client_id
14
16
  self.serial_number = serial_number
@@ -29,12 +31,12 @@ class OtaDevice(object):
29
31
  },
30
32
  }
31
33
  async with aiohttp.ClientSession() as session:
32
- async with session.post(OTA_URL, headers=header, data=json.dumps(payload)) as response:
34
+ async with session.post(self.ota_url, headers=header, data=json.dumps(payload)) as response:
33
35
  data = await response.json()
34
36
  return data
35
37
 
36
38
  async def check_activate(self, challenge: str):
37
- url = OTA_URL + "/activate"
39
+ url = self.ota_url + "/activate"
38
40
  header = {
39
41
  "user-agent": USER_AGENT,
40
42
  "Device-Id": self.mac_addr,
@@ -3,7 +3,7 @@ import json
3
3
  import requests
4
4
 
5
5
  from xiaozhi_sdk.config import VL_URL
6
- from xiaozhi_sdk.data import mcp_initialize_payload, mcp_tools_payload, mcp_tool_conf
6
+ from xiaozhi_sdk.data import mcp_initialize_payload, mcp_tool_conf, mcp_tools_payload
7
7
 
8
8
 
9
9
  class McpTool(object):
@@ -18,14 +18,16 @@ class McpTool(object):
18
18
  return json.dumps({"session_id": self.session_id, "type": "mcp", "payload": payload})
19
19
 
20
20
  def _build_response(self, request_id: str, content: str, is_error: bool = False):
21
- return self.get_mcp_json({
22
- "jsonrpc": "2.0",
23
- "id": request_id,
24
- "result": {
25
- "content": [{"type": "text", "text": content}],
26
- "isError": is_error,
27
- },
28
- })
21
+ return self.get_mcp_json(
22
+ {
23
+ "jsonrpc": "2.0",
24
+ "id": request_id,
25
+ "result": {
26
+ "content": [{"type": "text", "text": content}],
27
+ "isError": is_error,
28
+ },
29
+ }
30
+ )
29
31
 
30
32
  async def analyze_image(self, img_byte: bytes, question: str = "这张图片里有什么?"):
31
33
  headers = {"Authorization": f"Bearer {self.vl_token}"}
@@ -38,12 +40,12 @@ class McpTool(object):
38
40
  async def mcp_tool_call(self, mcp_json: dict):
39
41
  tool_name = mcp_json["params"]["name"]
40
42
  tool_func = self.tool_func[tool_name]
41
-
43
+
42
44
  if tool_name == "take_photo":
43
45
  res = await self.analyze_image(tool_func(None), mcp_json["params"]["arguments"]["question"])
44
46
  else:
45
47
  res = tool_func(mcp_json["params"]["arguments"])
46
-
48
+
47
49
  content = json.dumps(res, ensure_ascii=False)
48
50
  return self._build_response(mcp_json["id"], content)
49
51
 
@@ -1,4 +1,5 @@
1
1
  import os
2
+
2
3
  from xiaozhi_sdk import INPUT_SERVER_AUDIO_SAMPLE_RATE
3
4
 
4
5
  # 设置 opus 库路径
@@ -5,13 +5,14 @@ def get_wav_info(file_path):
5
5
  with wave.open(file_path, "rb") as wav_file:
6
6
  return wav_file.getframerate(), wav_file.getnchannels()
7
7
 
8
+
8
9
  def read_audio_file(file_path):
9
10
  """
10
11
  读取音频文件并通过yield返回PCM流
11
-
12
+
12
13
  Args:
13
14
  file_path (str): 音频文件路径
14
-
15
+
15
16
  Yields:
16
17
  bytes: PCM音频数据块
17
18
  """
@@ -20,4 +21,4 @@ def read_audio_file(file_path):
20
21
  pcm = wav_file.readframes(960) # 每次读取960帧(60ms的音频数据)
21
22
  if not pcm:
22
23
  break
23
- yield pcm
24
+ yield pcm
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.4
2
+ Name: xiaozhi-sdk
3
+ Version: 1.0.0
4
+ Summary: 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
5
+ Home-page: https://github.com/dairoot/xiaozhi-sdk
6
+ Author: dairoot
7
+ Author-email: 623815825@qq.com
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: numpy
15
+ Requires-Dist: websockets
16
+ Requires-Dist: aiohttp
17
+ Requires-Dist: av
18
+ Requires-Dist: opuslib
19
+ Requires-Dist: requests
20
+ Requires-Dist: sounddevice
21
+ Requires-Dist: python-socks
22
+ Dynamic: author
23
+ Dynamic: author-email
24
+ Dynamic: classifier
25
+ Dynamic: description
26
+ Dynamic: description-content-type
27
+ Dynamic: home-page
28
+ Dynamic: license-file
29
+ Dynamic: requires-dist
30
+ Dynamic: requires-python
31
+ Dynamic: summary
32
+
33
+ # 小智SDK (XiaoZhi SDK)
34
+
35
+ [![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
36
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
37
+ [![PyPI](https://img.shields.io/badge/pypi-xiaozhi--sdk-blue.svg)](https://pypi.org/project/xiaozhi-sdk/)
38
+
39
+ 一个用于连接和控制小智服务的 Python SDK,支持实时音频通信、MCP 工具集成和设备管理等功能。
40
+
41
+ ---
42
+
43
+ ## 📦 安装
44
+
45
+ ```bash
46
+ pip install xiaozhi-sdk
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 🚀 快速开始
52
+
53
+ ### 1. 终端使用
54
+
55
+ 最简单的方式是通过命令行连接设备:
56
+
57
+ #### 查看帮助信息
58
+
59
+ ```bash
60
+ python -m xiaozhi_sdk -h
61
+ ```
62
+
63
+ 输出示例:
64
+
65
+ ```text
66
+ positional arguments:
67
+ device 你的小智设备的MAC地址 (格式: XX:XX:XX:XX:XX:XX)
68
+
69
+ options:
70
+ -h, --help 显示帮助信息并退出
71
+ --url URL 小智服务 websocket 地址
72
+ --ota_url OTA_URL 小智 OTA 地址
73
+ ```
74
+
75
+ #### 连接设备(需要提供 MAC 地址)
76
+
77
+ ```bash
78
+ python -m xiaozhi_sdk 00:11:22:33:44:55
79
+ ```
80
+
81
+ ### 2. 编程使用
82
+ 参考 [examples](examples/) 文件中的示例代码,可以快速开始使用 SDK。
83
+
84
+
85
+
@@ -1,4 +1,6 @@
1
+ LICENSE
1
2
  README.md
3
+ setup.cfg
2
4
  setup.py
3
5
  tests/test_pic.py
4
6
  tests/test_xiaozhi.py
@@ -0,0 +1,8 @@
1
+ numpy
2
+ websockets
3
+ aiohttp
4
+ av
5
+ opuslib
6
+ requests
7
+ sounddevice
8
+ python-socks
@@ -1,58 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: xiaozhi-sdk
3
- Version: 0.1.0
4
- Summary: A short description of your package
5
- Home-page: https://github.com/dairoot/xiaozhi-sdk
6
- Author: dairoot
7
- Author-email: 623815825@qq.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
- Requires-Dist: numpy
14
- Requires-Dist: requests>=2.32.1
15
- Requires-Dist: sounddevice>=0.4.2
16
- Dynamic: author
17
- Dynamic: author-email
18
- Dynamic: classifier
19
- Dynamic: description
20
- Dynamic: description-content-type
21
- Dynamic: home-page
22
- Dynamic: requires-dist
23
- Dynamic: requires-python
24
- Dynamic: summary
25
-
26
- # 小智SDK (XiaoZhi SDK)
27
-
28
- [![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
29
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
30
- [![PyPI](https://img.shields.io/badge/pypi-xiaozhi--sdk-blue.svg)](https://pypi.org/project/xiaozhi-sdk/)
31
-
32
- 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
33
-
34
-
35
- ## 📦 安装
36
-
37
- ```bash
38
- pip install xiaozhi-sdk
39
- ```
40
-
41
-
42
- ## 🚀 快速开始
43
-
44
- ### 命令行使用
45
-
46
- 最简单的使用方式是通过命令行连接设备:
47
-
48
- ```bash
49
- # 查看帮助信息
50
- python -m xiaozhi_sdk -h
51
-
52
- # 连接设备(需要提供MAC地址)
53
- python -m xiaozhi_sdk 00:11:22:33:44:55
54
- ```
55
-
56
- ### 编程使用
57
- ...
58
-
@@ -1,33 +0,0 @@
1
- # 小智SDK (XiaoZhi SDK)
2
-
3
- [![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
4
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
5
- [![PyPI](https://img.shields.io/badge/pypi-xiaozhi--sdk-blue.svg)](https://pypi.org/project/xiaozhi-sdk/)
6
-
7
- 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
8
-
9
-
10
- ## 📦 安装
11
-
12
- ```bash
13
- pip install xiaozhi-sdk
14
- ```
15
-
16
-
17
- ## 🚀 快速开始
18
-
19
- ### 命令行使用
20
-
21
- 最简单的使用方式是通过命令行连接设备:
22
-
23
- ```bash
24
- # 查看帮助信息
25
- python -m xiaozhi_sdk -h
26
-
27
- # 连接设备(需要提供MAC地址)
28
- python -m xiaozhi_sdk 00:11:22:33:44:55
29
- ```
30
-
31
- ### 编程使用
32
- ...
33
-
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
@@ -1,58 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: xiaozhi-sdk
3
- Version: 0.1.0
4
- Summary: A short description of your package
5
- Home-page: https://github.com/dairoot/xiaozhi-sdk
6
- Author: dairoot
7
- Author-email: 623815825@qq.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
- Requires-Dist: numpy
14
- Requires-Dist: requests>=2.32.1
15
- Requires-Dist: sounddevice>=0.4.2
16
- Dynamic: author
17
- Dynamic: author-email
18
- Dynamic: classifier
19
- Dynamic: description
20
- Dynamic: description-content-type
21
- Dynamic: home-page
22
- Dynamic: requires-dist
23
- Dynamic: requires-python
24
- Dynamic: summary
25
-
26
- # 小智SDK (XiaoZhi SDK)
27
-
28
- [![Python Version](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
29
- [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
30
- [![PyPI](https://img.shields.io/badge/pypi-xiaozhi--sdk-blue.svg)](https://pypi.org/project/xiaozhi-sdk/)
31
-
32
- 一个用于连接和控制小智智能设备的Python SDK,支持实时音频通信、MCP工具集成和设备管理功能。
33
-
34
-
35
- ## 📦 安装
36
-
37
- ```bash
38
- pip install xiaozhi-sdk
39
- ```
40
-
41
-
42
- ## 🚀 快速开始
43
-
44
- ### 命令行使用
45
-
46
- 最简单的使用方式是通过命令行连接设备:
47
-
48
- ```bash
49
- # 查看帮助信息
50
- python -m xiaozhi_sdk -h
51
-
52
- # 连接设备(需要提供MAC地址)
53
- python -m xiaozhi_sdk 00:11:22:33:44:55
54
- ```
55
-
56
- ### 编程使用
57
- ...
58
-
@@ -1,3 +0,0 @@
1
- numpy
2
- requests>=2.32.1
3
- sounddevice>=0.4.2