mijiaAPI 4.0.0__tar.gz → 4.1.1__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.
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/.gitignore +1 -1
- {mijiaapi-4.0.0/mijiaAPI.egg-info → mijiaapi-4.1.1}/PKG-INFO +23 -3
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/README.md +22 -2
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/__main__.py +39 -20
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/apis.py +37 -7
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/mcp_server.py +110 -25
- mijiaapi-4.1.1/mijiaAPI/version.py +1 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1/mijiaAPI.egg-info}/PKG-INFO +23 -3
- mijiaapi-4.0.0/mijiaAPI/version.py +0 -1
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/LICENSE +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/demos/test_apis.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/demos/test_get_statistics.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/demos/test_login.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/__init__.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/devices.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/errors.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/logger.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI/miutils.py +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI.egg-info/SOURCES.txt +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI.egg-info/dependency_links.txt +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI.egg-info/entry_points.txt +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI.egg-info/requires.txt +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/mijiaAPI.egg-info/top_level.txt +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/pyproject.toml +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/setup.cfg +0 -0
- {mijiaapi-4.0.0 → mijiaapi-4.1.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mijiaAPI
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: A Python API for Xiaomi Mijia
|
|
5
5
|
Author-email: Do1e <i@do1e.cn>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -21,7 +21,10 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
# mijiaAPI
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
米家 API,可以使用代码、CLI、MCP 直接控制米家设备。
|
|
25
|
+
|
|
26
|
+
> 🎉 **v4.0.0 版本已支持 MCP**,详见 [MCP 使用文档](https://mijia-api.do1e.com/usage/mcp)
|
|
27
|
+
> 🎉 **v4.1.0 版本已支持 Agent Skill**,详见 [Agent Skill 使用文档](https://mijia-api.do1e.com/usage/skill)
|
|
25
28
|
|
|
26
29
|
[](https://github.com/Do1e/mijia-api)
|
|
27
30
|
[](https://pypi.org/project/mijiaAPI/)
|
|
@@ -29,6 +32,8 @@ Dynamic: license-file
|
|
|
29
32
|
|
|
30
33
|
📖 **完整文档请见 [mijia-api.do1e.com](https://mijia-api.do1e.com)**
|
|
31
34
|
|
|
35
|
+
[常见问题](https://mijia-api.do1e.com/faq) | [更新日志](https://mijia-api.do1e.com/changelog)
|
|
36
|
+
|
|
32
37
|
## 安装
|
|
33
38
|
|
|
34
39
|
> 要求 Python >= 3.10
|
|
@@ -38,7 +43,7 @@ pip install mijiaAPI
|
|
|
38
43
|
# Or `uv add mijiaAPI` for uv users
|
|
39
44
|
```
|
|
40
45
|
|
|
41
|
-
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com)。
|
|
46
|
+
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com/guide/installation)。
|
|
42
47
|
|
|
43
48
|
## 快速开始
|
|
44
49
|
|
|
@@ -66,6 +71,21 @@ mijiaAPI -l # 列出所有设备
|
|
|
66
71
|
mijiaAPI set --dev_name "台灯" --prop_name "brightness" --value 60
|
|
67
72
|
```
|
|
68
73
|
|
|
74
|
+
MCP 用法:
|
|
75
|
+
|
|
76
|
+
执行 `uvx mijiaAPI login -p /path/to/auth.json` 登录后,在 MCP 客户端配置中添加以下内容即可接入米家:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"mijia-api": {
|
|
82
|
+
"command": "uvx",
|
|
83
|
+
"args": ["mijiaAPI", "mcp", "-p", "/path/to/auth.json"]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
69
89
|
更多用法(API 基础调用、MCP Server、CLI 完整参数、最佳实践等)请查阅[完整文档](https://mijia-api.do1e.com)。
|
|
70
90
|
|
|
71
91
|
## 致谢
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
# mijiaAPI
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
米家 API,可以使用代码、CLI、MCP 直接控制米家设备。
|
|
4
|
+
|
|
5
|
+
> 🎉 **v4.0.0 版本已支持 MCP**,详见 [MCP 使用文档](https://mijia-api.do1e.com/usage/mcp)
|
|
6
|
+
> 🎉 **v4.1.0 版本已支持 Agent Skill**,详见 [Agent Skill 使用文档](https://mijia-api.do1e.com/usage/skill)
|
|
4
7
|
|
|
5
8
|
[](https://github.com/Do1e/mijia-api)
|
|
6
9
|
[](https://pypi.org/project/mijiaAPI/)
|
|
@@ -8,6 +11,8 @@
|
|
|
8
11
|
|
|
9
12
|
📖 **完整文档请见 [mijia-api.do1e.com](https://mijia-api.do1e.com)**
|
|
10
13
|
|
|
14
|
+
[常见问题](https://mijia-api.do1e.com/faq) | [更新日志](https://mijia-api.do1e.com/changelog)
|
|
15
|
+
|
|
11
16
|
## 安装
|
|
12
17
|
|
|
13
18
|
> 要求 Python >= 3.10
|
|
@@ -17,7 +22,7 @@ pip install mijiaAPI
|
|
|
17
22
|
# Or `uv add mijiaAPI` for uv users
|
|
18
23
|
```
|
|
19
24
|
|
|
20
|
-
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com)。
|
|
25
|
+
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com/guide/installation)。
|
|
21
26
|
|
|
22
27
|
## 快速开始
|
|
23
28
|
|
|
@@ -45,6 +50,21 @@ mijiaAPI -l # 列出所有设备
|
|
|
45
50
|
mijiaAPI set --dev_name "台灯" --prop_name "brightness" --value 60
|
|
46
51
|
```
|
|
47
52
|
|
|
53
|
+
MCP 用法:
|
|
54
|
+
|
|
55
|
+
执行 `uvx mijiaAPI login -p /path/to/auth.json` 登录后,在 MCP 客户端配置中添加以下内容即可接入米家:
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"mijia-api": {
|
|
61
|
+
"command": "uvx",
|
|
62
|
+
"args": ["mijiaAPI", "mcp", "-p", "/path/to/auth.json"]
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
48
68
|
更多用法(API 基础调用、MCP Server、CLI 完整参数、最佳实践等)请查阅[完整文档](https://mijia-api.do1e.com)。
|
|
49
69
|
|
|
50
70
|
## 致谢
|
|
@@ -193,26 +193,27 @@ def parse_args(args):
|
|
|
193
193
|
return parser.parse_args(args)
|
|
194
194
|
|
|
195
195
|
def init_api(auth_path: Path) -> mijiaAPI:
|
|
196
|
-
class APIUnavailableError(Exception):
|
|
197
|
-
pass
|
|
198
|
-
|
|
199
196
|
if Path(auth_path).is_dir():
|
|
200
197
|
auth_path = auth_path / "auth.json"
|
|
201
|
-
if auth_path.exists():
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if not api.available:
|
|
207
|
-
raise APIUnavailableError()
|
|
208
|
-
return api
|
|
209
|
-
except (json.JSONDecodeError, APIUnavailableError):
|
|
210
|
-
auth_path.unlink(missing_ok=True)
|
|
211
|
-
api = mijiaAPI(auth_data_path=auth_path)
|
|
212
|
-
api.login()
|
|
213
|
-
else:
|
|
198
|
+
if not auth_path.exists():
|
|
199
|
+
print(f"认证文件不存在: {auth_path}")
|
|
200
|
+
print("请调用 'mijiaAPI login' 进行扫描登录")
|
|
201
|
+
sys.exit(1)
|
|
202
|
+
try:
|
|
214
203
|
api = mijiaAPI(auth_data_path=auth_path)
|
|
215
|
-
|
|
204
|
+
except json.JSONDecodeError:
|
|
205
|
+
print(f"认证文件已损坏: {auth_path}")
|
|
206
|
+
print("请调用 'mijiaAPI login' 进行扫描登录")
|
|
207
|
+
sys.exit(1)
|
|
208
|
+
if not api.available:
|
|
209
|
+
try:
|
|
210
|
+
api._refresh_token()
|
|
211
|
+
except Exception:
|
|
212
|
+
pass
|
|
213
|
+
if not api.available:
|
|
214
|
+
print(f"认证已失效且刷新失败: {auth_path}")
|
|
215
|
+
print("请调用 'mijiaAPI login' 进行扫描登录")
|
|
216
|
+
sys.exit(1)
|
|
216
217
|
return api
|
|
217
218
|
|
|
218
219
|
def get_homes_list(api: mijiaAPI, verbose: bool = True, device_mapping: Optional[dict] = None) -> dict:
|
|
@@ -245,14 +246,24 @@ def get_homes_list(api: mijiaAPI, verbose: bool = True, device_mapping: Optional
|
|
|
245
246
|
home_mapping = {home['id']: home for home in homes}
|
|
246
247
|
return home_mapping
|
|
247
248
|
|
|
248
|
-
def get_devices_list(api: mijiaAPI, verbose: bool = True) -> dict:
|
|
249
|
+
def get_devices_list(api: mijiaAPI, verbose: bool = True, home_mapping: Optional[dict] = None) -> dict:
|
|
249
250
|
devices = api.get_devices_list() + api.get_shared_devices_list()
|
|
250
251
|
if verbose:
|
|
252
|
+
if home_mapping is None:
|
|
253
|
+
home_mapping = get_homes_list(api, verbose=False)
|
|
254
|
+
did_location = {}
|
|
255
|
+
for home in home_mapping.values():
|
|
256
|
+
for room in home.get('roomlist', []):
|
|
257
|
+
for did in room.get('dids', []) or []:
|
|
258
|
+
did_location[did] = (home['name'], room['name'])
|
|
251
259
|
print("设备列表:")
|
|
252
260
|
for device in devices:
|
|
261
|
+
home_name, room_name = did_location.get(device['did'], ("未知", "未知"))
|
|
253
262
|
print(f" - {device['name']}\n"
|
|
254
263
|
f" did: {device['did']}\n"
|
|
255
264
|
f" model: {device['model']}\n"
|
|
265
|
+
f" home: {home_name}\n"
|
|
266
|
+
f" room: {room_name}\n"
|
|
256
267
|
f" online: {device['isOnline']}")
|
|
257
268
|
device_mapping = {device['did']: device for device in devices}
|
|
258
269
|
return device_mapping
|
|
@@ -345,7 +356,13 @@ def main(args):
|
|
|
345
356
|
run_mcp(args.auth_path)
|
|
346
357
|
return
|
|
347
358
|
if hasattr(args, 'func') and args.func == 'login':
|
|
348
|
-
|
|
359
|
+
auth_path = args.auth_path
|
|
360
|
+
file_path = Path(auth_path) / "auth.json" if Path(auth_path).is_dir() else Path(auth_path)
|
|
361
|
+
try:
|
|
362
|
+
api = mijiaAPI(auth_data_path=auth_path)
|
|
363
|
+
except json.JSONDecodeError:
|
|
364
|
+
file_path.unlink(missing_ok=True)
|
|
365
|
+
api = mijiaAPI(auth_data_path=auth_path)
|
|
349
366
|
if not api.available:
|
|
350
367
|
api.login()
|
|
351
368
|
return
|
|
@@ -356,7 +373,9 @@ def main(args):
|
|
|
356
373
|
scenes_mapping = None
|
|
357
374
|
|
|
358
375
|
if args.list_devices:
|
|
359
|
-
|
|
376
|
+
if home_mapping is None:
|
|
377
|
+
home_mapping = get_homes_list(api, verbose=False)
|
|
378
|
+
device_mapping = get_devices_list(api, home_mapping=home_mapping)
|
|
360
379
|
if args.list_homes:
|
|
361
380
|
home_mapping = get_homes_list(api, device_mapping=device_mapping)
|
|
362
381
|
if args.list_scenes:
|
|
@@ -226,15 +226,30 @@ class mijiaAPI():
|
|
|
226
226
|
异常:
|
|
227
227
|
LoginError: 当登录超时或服务器返回错误时抛出
|
|
228
228
|
"""
|
|
229
|
-
|
|
229
|
+
login_data = self._get_qr_login_data()
|
|
230
|
+
if login_data.get("refreshed"):
|
|
231
|
+
return self.auth_data
|
|
232
|
+
self._print_qr(login_data["loginUrl"])
|
|
233
|
+
print(f"也可以访问链接查看二维码图片: {login_data['qr']}")
|
|
234
|
+
return self._complete_qr_login(login_data)
|
|
235
|
+
|
|
236
|
+
def _get_qr_login_data(self) -> dict:
|
|
237
|
+
"""获取二维码登录数据(含 loginUrl/qr/lp),不阻塞等待扫码。
|
|
238
|
+
|
|
239
|
+
先尝试刷新 token,若成功返回 {"refreshed": True};否则请求二维码并返回
|
|
240
|
+
登录数据,供 _complete_qr_login 长轮询完成登录。
|
|
241
|
+
|
|
242
|
+
返回值:
|
|
243
|
+
dict: 刷新成功时为 {"refreshed": True};否则包含 loginUrl(二维码原始
|
|
244
|
+
链接)、qr(二维码图片链接)、lp(长轮询地址)等字段。
|
|
245
|
+
"""
|
|
230
246
|
location_data = self._get_location()
|
|
231
247
|
if location_data.get("code", -1) == 0 and location_data.get("message", "") == "刷新Token成功":
|
|
232
248
|
self._save_auth_data()
|
|
233
249
|
self._init_session()
|
|
234
250
|
logger.info("刷新Token成功,无需登录")
|
|
235
|
-
return
|
|
251
|
+
return {"refreshed": True}
|
|
236
252
|
|
|
237
|
-
# Step 2: 获取并打印二维码
|
|
238
253
|
location_data.update({
|
|
239
254
|
"theme": "",
|
|
240
255
|
"bizDeviceType": "",
|
|
@@ -251,10 +266,26 @@ class mijiaAPI():
|
|
|
251
266
|
}
|
|
252
267
|
login_ret = requests.get(url, headers=headers)
|
|
253
268
|
login_data = self._handle_ret(login_ret)
|
|
254
|
-
|
|
255
|
-
|
|
269
|
+
return login_data
|
|
270
|
+
|
|
271
|
+
def _complete_qr_login(self, login_data: dict) -> dict:
|
|
272
|
+
"""长轮询等待扫码并完成登录,保存认证数据。
|
|
256
273
|
|
|
257
|
-
|
|
274
|
+
参数:
|
|
275
|
+
login_data: _get_qr_login_data 返回的登录数据(含 lp 等字段)。
|
|
276
|
+
|
|
277
|
+
返回值:
|
|
278
|
+
dict: 包含认证信息的字典。
|
|
279
|
+
|
|
280
|
+
异常:
|
|
281
|
+
LoginError: 当扫码超时或服务器返回错误时抛出。
|
|
282
|
+
"""
|
|
283
|
+
headers = {
|
|
284
|
+
"User-Agent": self.user_agent,
|
|
285
|
+
"Accept-Encoding": "gzip",
|
|
286
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
287
|
+
"Connection": "keep-alive",
|
|
288
|
+
}
|
|
258
289
|
session = requests.Session()
|
|
259
290
|
try:
|
|
260
291
|
lp_ret = session.get(login_data["lp"], headers=headers, timeout=120)
|
|
@@ -262,7 +293,6 @@ class mijiaAPI():
|
|
|
262
293
|
except requests.exceptions.Timeout:
|
|
263
294
|
raise LoginError(-1, "超时,请重试")
|
|
264
295
|
|
|
265
|
-
# Step 4: 处理登录结果
|
|
266
296
|
auth_keys = ["psecurity", "nonce", "ssecurity", "passToken", "userId", "cUserId"]
|
|
267
297
|
for key in auth_keys:
|
|
268
298
|
self.auth_data[key] = lp_data[key]
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
|
+
import threading
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from typing import Optional
|
|
6
7
|
|
|
@@ -10,19 +11,25 @@ from .apis import mijiaAPI
|
|
|
10
11
|
from .devices import get_device_info, mijiaDevice
|
|
11
12
|
from .errors import LoginError
|
|
12
13
|
from .logger import logger
|
|
14
|
+
from .version import version
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
mcp = FastMCP("mijia-api")
|
|
17
|
+
mcp = FastMCP("mijia-api", version=version)
|
|
16
18
|
|
|
17
19
|
_api: Optional[mijiaAPI] = None
|
|
18
20
|
_auth_path: Optional[Path] = None
|
|
19
21
|
|
|
22
|
+
_login_api: Optional[mijiaAPI] = None
|
|
23
|
+
_login_data: Optional[dict] = None
|
|
24
|
+
_login_thread: Optional[threading.Thread] = None
|
|
25
|
+
_login_status: dict = {"status": "idle"}
|
|
26
|
+
|
|
20
27
|
|
|
21
28
|
def _get_api() -> mijiaAPI:
|
|
22
29
|
global _api
|
|
23
30
|
if _api is not None:
|
|
24
31
|
return _api
|
|
25
|
-
raise RuntimeError("mijiaAPI
|
|
32
|
+
raise RuntimeError("mijiaAPI 未初始化,请先调用 login 工具完成登录")
|
|
26
33
|
|
|
27
34
|
|
|
28
35
|
def _refresh_if_needed(api: mijiaAPI) -> None:
|
|
@@ -31,8 +38,7 @@ def _refresh_if_needed(api: mijiaAPI) -> None:
|
|
|
31
38
|
api._refresh_token()
|
|
32
39
|
except LoginError:
|
|
33
40
|
raise RuntimeError(
|
|
34
|
-
"
|
|
35
|
-
f"`mijiaAPI login -p {_auth_path}` 重新登录后重启 MCP server"
|
|
41
|
+
"认证已失效且无法自动刷新,请调用 login 工具重新登录"
|
|
36
42
|
)
|
|
37
43
|
|
|
38
44
|
|
|
@@ -40,30 +46,28 @@ def run(auth_path: Path) -> None:
|
|
|
40
46
|
global _api, _auth_path
|
|
41
47
|
_auth_path = auth_path if not auth_path.is_dir() else auth_path / "auth.json"
|
|
42
48
|
|
|
43
|
-
if not _auth_path.exists():
|
|
44
|
-
print(
|
|
45
|
-
f"认证文件不存在: {_auth_path}\n"
|
|
46
|
-
f"请先运行 `mijiaAPI login -p {_auth_path}` 登录后再启动 MCP server",
|
|
47
|
-
file=sys.stderr,
|
|
48
|
-
)
|
|
49
|
-
sys.exit(1)
|
|
50
|
-
|
|
51
49
|
logger.handlers = [h for h in logger.handlers if not isinstance(h, logging.StreamHandler) or h.stream is sys.stderr]
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
raise LoginError(-1, "认证不可用")
|
|
58
|
-
except Exception as e:
|
|
59
|
-
print(
|
|
60
|
-
f"认证不可用且无法自动刷新: {e}\n"
|
|
61
|
-
f"请运行 `mijiaAPI login -p {_auth_path}` 重新登录后再启动 MCP server",
|
|
62
|
-
file=sys.stderr,
|
|
50
|
+
|
|
51
|
+
if not _auth_path.exists():
|
|
52
|
+
_api = None
|
|
53
|
+
logger.warning(
|
|
54
|
+
f"认证文件不存在: {_auth_path},请调用 login 工具完成登录后再使用其他工具"
|
|
63
55
|
)
|
|
64
|
-
|
|
56
|
+
else:
|
|
57
|
+
try:
|
|
58
|
+
_api = mijiaAPI(auth_data_path=_auth_path)
|
|
59
|
+
if not _api.available:
|
|
60
|
+
_api._refresh_token()
|
|
61
|
+
if not _api.available:
|
|
62
|
+
raise LoginError(-1, "认证不可用")
|
|
63
|
+
logger.info(f"MCP server 启动,认证文件: {_auth_path}")
|
|
64
|
+
except Exception as e:
|
|
65
|
+
_api = None
|
|
66
|
+
logger.warning(
|
|
67
|
+
f"认证不可用且无法自动刷新: {e}\n"
|
|
68
|
+
f"请调用 login 工具重新登录后再使用其他工具"
|
|
69
|
+
)
|
|
65
70
|
|
|
66
|
-
logger.info(f"MCP server 启动,认证文件: {_auth_path}")
|
|
67
71
|
mcp.run()
|
|
68
72
|
|
|
69
73
|
|
|
@@ -318,3 +322,84 @@ def run_speaker_command(
|
|
|
318
322
|
speaker = mijiaDevice(api, did=match["did"])
|
|
319
323
|
speaker.run_action("execute-text-directive", _in=[prompt, 1 if quiet else 0])
|
|
320
324
|
return f"已通过 {match['name']} 执行: {prompt}"
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _login_worker(api: mijiaAPI, login_data: dict) -> None:
|
|
328
|
+
try:
|
|
329
|
+
api._complete_qr_login(login_data)
|
|
330
|
+
_login_status.update({"status": "success", "message": "登录成功"})
|
|
331
|
+
except LoginError as e:
|
|
332
|
+
_login_status.update({"status": "error", "message": f"登录失败: {e}"})
|
|
333
|
+
except Exception as e:
|
|
334
|
+
_login_status.update({"status": "error", "message": f"登录失败: {e}"})
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@mcp.tool
|
|
338
|
+
def login() -> str:
|
|
339
|
+
"""发起米家二维码登录。
|
|
340
|
+
|
|
341
|
+
当 API 调用失败或凭证过期且自动刷新失败时使用。先尝试刷新 token,
|
|
342
|
+
失败则生成二维码并在后台长轮询等待扫码。返回二维码图片链接,请用
|
|
343
|
+
米家APP在2分钟内扫码,然后调用 login_status 查询结果。
|
|
344
|
+
"""
|
|
345
|
+
global _api, _login_api, _login_data, _login_thread, _login_status
|
|
346
|
+
if _auth_path is None:
|
|
347
|
+
return "认证路径未初始化,请检查 MCP server 启动配置"
|
|
348
|
+
if _login_thread is not None and _login_thread.is_alive():
|
|
349
|
+
return "已有登录正在进行中,请调用 login_status 查询结果"
|
|
350
|
+
|
|
351
|
+
if _api is not None:
|
|
352
|
+
try:
|
|
353
|
+
if _api.available:
|
|
354
|
+
return "凭证仍然有效,无需重新登录"
|
|
355
|
+
_api._refresh_token()
|
|
356
|
+
if _api.available:
|
|
357
|
+
return "Token 刷新成功,无需重新登录"
|
|
358
|
+
except LoginError:
|
|
359
|
+
pass
|
|
360
|
+
|
|
361
|
+
new_api = mijiaAPI(auth_data_path=_auth_path)
|
|
362
|
+
login_data = new_api._get_qr_login_data()
|
|
363
|
+
if login_data.get("refreshed"):
|
|
364
|
+
_api = new_api
|
|
365
|
+
return "Token 刷新成功,无需重新登录"
|
|
366
|
+
|
|
367
|
+
_login_api = new_api
|
|
368
|
+
_login_data = login_data
|
|
369
|
+
_login_status = {"status": "pending", "message": "等待扫码"}
|
|
370
|
+
_login_thread = threading.Thread(target=_login_worker, args=(new_api, login_data), daemon=True)
|
|
371
|
+
_login_thread.start()
|
|
372
|
+
return (
|
|
373
|
+
"二维码已生成,请在2分钟内用米家APP扫码完成登录。\n"
|
|
374
|
+
f"二维码图片链接: {login_data['qr']}\n"
|
|
375
|
+
"扫码后请调用 login_status 查询登录结果。"
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
@mcp.tool
|
|
380
|
+
def login_status() -> str:
|
|
381
|
+
"""查询 login 发起的二维码登录结果。
|
|
382
|
+
|
|
383
|
+
返回 pending(等待扫码)/ success(登录成功,已切换为新凭证)/ error(失败)。
|
|
384
|
+
成功后后续工具调用将使用新登录的凭证。
|
|
385
|
+
"""
|
|
386
|
+
global _api, _login_api, _login_data, _login_thread, _login_status
|
|
387
|
+
if _login_thread is None:
|
|
388
|
+
return "没有正在进行的登录,请先调用 login"
|
|
389
|
+
|
|
390
|
+
status = _login_status.get("status", "idle")
|
|
391
|
+
if status == "success":
|
|
392
|
+
_api = _login_api
|
|
393
|
+
_login_thread = None
|
|
394
|
+
_login_api = None
|
|
395
|
+
_login_data = None
|
|
396
|
+
_login_status = {"status": "idle"}
|
|
397
|
+
return "登录成功,已切换为新凭证,可继续调用其他工具"
|
|
398
|
+
if status == "error":
|
|
399
|
+
message = _login_status.get("message", "登录失败")
|
|
400
|
+
_login_thread = None
|
|
401
|
+
_login_api = None
|
|
402
|
+
_login_data = None
|
|
403
|
+
_login_status = {"status": "idle"}
|
|
404
|
+
return message
|
|
405
|
+
return "等待扫码中,请用米家APP扫描 login 返回的二维码后再次查询"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "4.1.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mijiaAPI
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.1.1
|
|
4
4
|
Summary: A Python API for Xiaomi Mijia
|
|
5
5
|
Author-email: Do1e <i@do1e.cn>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -21,7 +21,10 @@ Dynamic: license-file
|
|
|
21
21
|
|
|
22
22
|
# mijiaAPI
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
米家 API,可以使用代码、CLI、MCP 直接控制米家设备。
|
|
25
|
+
|
|
26
|
+
> 🎉 **v4.0.0 版本已支持 MCP**,详见 [MCP 使用文档](https://mijia-api.do1e.com/usage/mcp)
|
|
27
|
+
> 🎉 **v4.1.0 版本已支持 Agent Skill**,详见 [Agent Skill 使用文档](https://mijia-api.do1e.com/usage/skill)
|
|
25
28
|
|
|
26
29
|
[](https://github.com/Do1e/mijia-api)
|
|
27
30
|
[](https://pypi.org/project/mijiaAPI/)
|
|
@@ -29,6 +32,8 @@ Dynamic: license-file
|
|
|
29
32
|
|
|
30
33
|
📖 **完整文档请见 [mijia-api.do1e.com](https://mijia-api.do1e.com)**
|
|
31
34
|
|
|
35
|
+
[常见问题](https://mijia-api.do1e.com/faq) | [更新日志](https://mijia-api.do1e.com/changelog)
|
|
36
|
+
|
|
32
37
|
## 安装
|
|
33
38
|
|
|
34
39
|
> 要求 Python >= 3.10
|
|
@@ -38,7 +43,7 @@ pip install mijiaAPI
|
|
|
38
43
|
# Or `uv add mijiaAPI` for uv users
|
|
39
44
|
```
|
|
40
45
|
|
|
41
|
-
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com)。
|
|
46
|
+
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com/guide/installation)。
|
|
42
47
|
|
|
43
48
|
## 快速开始
|
|
44
49
|
|
|
@@ -66,6 +71,21 @@ mijiaAPI -l # 列出所有设备
|
|
|
66
71
|
mijiaAPI set --dev_name "台灯" --prop_name "brightness" --value 60
|
|
67
72
|
```
|
|
68
73
|
|
|
74
|
+
MCP 用法:
|
|
75
|
+
|
|
76
|
+
执行 `uvx mijiaAPI login -p /path/to/auth.json` 登录后,在 MCP 客户端配置中添加以下内容即可接入米家:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
{
|
|
80
|
+
"mcpServers": {
|
|
81
|
+
"mijia-api": {
|
|
82
|
+
"command": "uvx",
|
|
83
|
+
"args": ["mijiaAPI", "mcp", "-p", "/path/to/auth.json"]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
69
89
|
更多用法(API 基础调用、MCP Server、CLI 完整参数、最佳实践等)请查阅[完整文档](https://mijia-api.do1e.com)。
|
|
70
90
|
|
|
71
91
|
## 致谢
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
version = "4.0.0"
|
|
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
|