mijiaAPI 3.2.0__tar.gz → 4.1.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.
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/.gitignore +4 -0
- mijiaapi-4.1.0/PKG-INFO +107 -0
- mijiaapi-4.1.0/README.md +86 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/__main__.py +58 -17
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/apis.py +37 -7
- mijiaapi-4.1.0/mijiaAPI/mcp_server.py +405 -0
- mijiaapi-4.1.0/mijiaAPI/version.py +1 -0
- mijiaapi-4.1.0/mijiaAPI.egg-info/PKG-INFO +107 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/SOURCES.txt +1 -2
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/requires.txt +1 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/pyproject.toml +2 -1
- mijiaapi-4.1.0/uv.lock +2139 -0
- mijiaapi-3.2.0/CHANGELOG.md +0 -161
- mijiaapi-3.2.0/FAQ.md +0 -24
- mijiaapi-3.2.0/PKG-INFO +0 -596
- mijiaapi-3.2.0/README.md +0 -576
- mijiaapi-3.2.0/mijiaAPI/version.py +0 -1
- mijiaapi-3.2.0/mijiaAPI.egg-info/PKG-INFO +0 -596
- mijiaapi-3.2.0/uv.lock +0 -749
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/LICENSE +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/demos/test_apis.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/demos/test_get_statistics.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/demos/test_login.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/__init__.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/devices.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/errors.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/logger.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/miutils.py +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/dependency_links.txt +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/entry_points.txt +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/top_level.txt +0 -0
- {mijiaapi-3.2.0 → mijiaapi-4.1.0}/setup.cfg +0 -0
mijiaapi-4.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mijiaAPI
|
|
3
|
+
Version: 4.1.0
|
|
4
|
+
Summary: A Python API for Xiaomi Mijia
|
|
5
|
+
Author-email: Do1e <i@do1e.cn>
|
|
6
|
+
License-Expression: GPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/Do1e/mijia-api
|
|
8
|
+
Project-URL: Repository, https://github.com/Do1e/mijia-api
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: <4.0,>=3.10
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: fastmcp>=3.4.2
|
|
15
|
+
Requires-Dist: pillow>=11.3.0
|
|
16
|
+
Requires-Dist: pycryptodome>=3.23.0
|
|
17
|
+
Requires-Dist: qrcode>=8.2
|
|
18
|
+
Requires-Dist: requests>=2.32.5
|
|
19
|
+
Requires-Dist: tzlocal>=5.3.1
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
# mijiaAPI
|
|
23
|
+
|
|
24
|
+
米家 API,可以使用代码、CLI、MCP 直接控制米家设备。
|
|
25
|
+
|
|
26
|
+
> 🎉 **v4 版本已支持 MCP**,详见 [MCP 使用文档](https://mijia-api.do1e.com/usage/mcp)
|
|
27
|
+
|
|
28
|
+
[](https://github.com/Do1e/mijia-api)
|
|
29
|
+
[](https://pypi.org/project/mijiaAPI/)
|
|
30
|
+
[](https://opensource.org/licenses/GPL-3.0)
|
|
31
|
+
|
|
32
|
+
📖 **完整文档请见 [mijia-api.do1e.com](https://mijia-api.do1e.com)**
|
|
33
|
+
|
|
34
|
+
[常见问题](https://mijia-api.do1e.com/faq) | [更新日志](https://mijia-api.do1e.com/changelog)
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
> 要求 Python >= 3.10
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install mijiaAPI
|
|
42
|
+
# Or `uv add mijiaAPI` for uv users
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com/guide/installation)。
|
|
46
|
+
|
|
47
|
+
## 快速开始
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
from mijiaAPI import mijiaAPI, mijiaDevice
|
|
51
|
+
|
|
52
|
+
# 初始化并扫码登录(认证文件默认保存在 ~/.config/mijia-api/auth.json)
|
|
53
|
+
api = mijiaAPI()
|
|
54
|
+
api.login()
|
|
55
|
+
|
|
56
|
+
# 通过设备名称控制设备(推荐)
|
|
57
|
+
device = mijiaDevice(api, dev_name="我的台灯")
|
|
58
|
+
device.on = True # 打开设备
|
|
59
|
+
device.brightness = 60 # 设置亮度为 60%
|
|
60
|
+
|
|
61
|
+
# 查看设备支持的所有属性和动作
|
|
62
|
+
print(device)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
CLI 用法:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
mijiaAPI login # 扫码登录
|
|
69
|
+
mijiaAPI -l # 列出所有设备
|
|
70
|
+
mijiaAPI set --dev_name "台灯" --prop_name "brightness" --value 60
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
MCP 用法:
|
|
74
|
+
|
|
75
|
+
执行 `uvx mijiaAPI login -p /path/to/auth.json` 登录后,在 MCP 客户端配置中添加以下内容即可接入米家:
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"mcpServers": {
|
|
80
|
+
"mijia-api": {
|
|
81
|
+
"command": "uvx",
|
|
82
|
+
"args": ["mijiaAPI", "mcp", "-p", "/path/to/auth.json"]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
更多用法(API 基础调用、MCP Server、CLI 完整参数、最佳实践等)请查阅[完整文档](https://mijia-api.do1e.com)。
|
|
89
|
+
|
|
90
|
+
## 致谢
|
|
91
|
+
|
|
92
|
+
* [janzlan/mijia-api](https://gitee.com/janzlan/mijia-api/tree/master)
|
|
93
|
+
* [米家 APP 网络请求的抓包、加解密与构造的代码笔记](https://imkero.net/posts/mihome-app-api/)
|
|
94
|
+
* [al-one/hass-xiaomi-miot](https://github.com/al-one/hass-xiaomi-miot)
|
|
95
|
+
|
|
96
|
+
## 开源许可
|
|
97
|
+
|
|
98
|
+
本项目采用 [GPL-3.0](LICENSE) 开源许可证。
|
|
99
|
+
|
|
100
|
+
**请注意:GPL-3.0 是具有“强传染性”的开源许可证。**
|
|
101
|
+
如果您在您的项目中使用、修改或分发本项目的代码(包括作为库依赖),您的整个项目也**必须**以 GPL-3.0 或兼容许可证开源发布。
|
|
102
|
+
|
|
103
|
+
## 免责声明
|
|
104
|
+
|
|
105
|
+
* 本项目仅供学习交流使用,不得用于商业用途,如有侵权请联系删除
|
|
106
|
+
* 用户使用本项目所产生的任何后果,需自行承担风险
|
|
107
|
+
* 开发者不对使用本项目产生的任何直接或间接损失负责
|
mijiaapi-4.1.0/README.md
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# mijiaAPI
|
|
2
|
+
|
|
3
|
+
米家 API,可以使用代码、CLI、MCP 直接控制米家设备。
|
|
4
|
+
|
|
5
|
+
> 🎉 **v4 版本已支持 MCP**,详见 [MCP 使用文档](https://mijia-api.do1e.com/usage/mcp)
|
|
6
|
+
|
|
7
|
+
[](https://github.com/Do1e/mijia-api)
|
|
8
|
+
[](https://pypi.org/project/mijiaAPI/)
|
|
9
|
+
[](https://opensource.org/licenses/GPL-3.0)
|
|
10
|
+
|
|
11
|
+
📖 **完整文档请见 [mijia-api.do1e.com](https://mijia-api.do1e.com)**
|
|
12
|
+
|
|
13
|
+
[常见问题](https://mijia-api.do1e.com/faq) | [更新日志](https://mijia-api.do1e.com/changelog)
|
|
14
|
+
|
|
15
|
+
## 安装
|
|
16
|
+
|
|
17
|
+
> 要求 Python >= 3.10
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install mijiaAPI
|
|
21
|
+
# Or `uv add mijiaAPI` for uv users
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
其他安装方式(源码安装、AUR)请参考[文档](https://mijia-api.do1e.com/guide/installation)。
|
|
25
|
+
|
|
26
|
+
## 快速开始
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from mijiaAPI import mijiaAPI, mijiaDevice
|
|
30
|
+
|
|
31
|
+
# 初始化并扫码登录(认证文件默认保存在 ~/.config/mijia-api/auth.json)
|
|
32
|
+
api = mijiaAPI()
|
|
33
|
+
api.login()
|
|
34
|
+
|
|
35
|
+
# 通过设备名称控制设备(推荐)
|
|
36
|
+
device = mijiaDevice(api, dev_name="我的台灯")
|
|
37
|
+
device.on = True # 打开设备
|
|
38
|
+
device.brightness = 60 # 设置亮度为 60%
|
|
39
|
+
|
|
40
|
+
# 查看设备支持的所有属性和动作
|
|
41
|
+
print(device)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
CLI 用法:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
mijiaAPI login # 扫码登录
|
|
48
|
+
mijiaAPI -l # 列出所有设备
|
|
49
|
+
mijiaAPI set --dev_name "台灯" --prop_name "brightness" --value 60
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
MCP 用法:
|
|
53
|
+
|
|
54
|
+
执行 `uvx mijiaAPI login -p /path/to/auth.json` 登录后,在 MCP 客户端配置中添加以下内容即可接入米家:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"mcpServers": {
|
|
59
|
+
"mijia-api": {
|
|
60
|
+
"command": "uvx",
|
|
61
|
+
"args": ["mijiaAPI", "mcp", "-p", "/path/to/auth.json"]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
更多用法(API 基础调用、MCP Server、CLI 完整参数、最佳实践等)请查阅[完整文档](https://mijia-api.do1e.com)。
|
|
68
|
+
|
|
69
|
+
## 致谢
|
|
70
|
+
|
|
71
|
+
* [janzlan/mijia-api](https://gitee.com/janzlan/mijia-api/tree/master)
|
|
72
|
+
* [米家 APP 网络请求的抓包、加解密与构造的代码笔记](https://imkero.net/posts/mihome-app-api/)
|
|
73
|
+
* [al-one/hass-xiaomi-miot](https://github.com/al-one/hass-xiaomi-miot)
|
|
74
|
+
|
|
75
|
+
## 开源许可
|
|
76
|
+
|
|
77
|
+
本项目采用 [GPL-3.0](LICENSE) 开源许可证。
|
|
78
|
+
|
|
79
|
+
**请注意:GPL-3.0 是具有“强传染性”的开源许可证。**
|
|
80
|
+
如果您在您的项目中使用、修改或分发本项目的代码(包括作为库依赖),您的整个项目也**必须**以 GPL-3.0 或兼容许可证开源发布。
|
|
81
|
+
|
|
82
|
+
## 免责声明
|
|
83
|
+
|
|
84
|
+
* 本项目仅供学习交流使用,不得用于商业用途,如有侵权请联系删除
|
|
85
|
+
* 用户使用本项目所产生的任何后果,需自行承担风险
|
|
86
|
+
* 开发者不对使用本项目产生的任何直接或间接损失负责
|
|
@@ -9,6 +9,7 @@ from typing import Optional
|
|
|
9
9
|
|
|
10
10
|
from .apis import mijiaAPI
|
|
11
11
|
from .devices import get_device_info, mijiaDevice
|
|
12
|
+
from .mcp_server import run as run_mcp
|
|
12
13
|
from .version import version
|
|
13
14
|
|
|
14
15
|
|
|
@@ -104,6 +105,30 @@ def parse_args(args):
|
|
|
104
105
|
help="小爱音箱静默执行",
|
|
105
106
|
)
|
|
106
107
|
|
|
108
|
+
mcp_cmd = subparsers.add_parser(
|
|
109
|
+
'mcp',
|
|
110
|
+
help="启动 MCP server(stdio 传输)",
|
|
111
|
+
)
|
|
112
|
+
mcp_cmd.set_defaults(func='mcp')
|
|
113
|
+
mcp_cmd.add_argument(
|
|
114
|
+
'-p', '--auth_path',
|
|
115
|
+
type=Path,
|
|
116
|
+
default=Path.home() / ".config" / "mijia-api" / "auth.json",
|
|
117
|
+
help="认证文件保存路径,默认保存在 ~/.config/mijia-api/auth.json",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
login_cmd = subparsers.add_parser(
|
|
121
|
+
'login',
|
|
122
|
+
help="二维码登录米家账号",
|
|
123
|
+
)
|
|
124
|
+
login_cmd.set_defaults(func='login')
|
|
125
|
+
login_cmd.add_argument(
|
|
126
|
+
'-p', '--auth_path',
|
|
127
|
+
type=Path,
|
|
128
|
+
default=Path.home() / ".config" / "mijia-api" / "auth.json",
|
|
129
|
+
help="认证文件保存路径,默认保存在 ~/.config/mijia-api/auth.json",
|
|
130
|
+
)
|
|
131
|
+
|
|
107
132
|
get = subparsers.add_parser(
|
|
108
133
|
'get',
|
|
109
134
|
help="获取设备属性",
|
|
@@ -168,26 +193,27 @@ def parse_args(args):
|
|
|
168
193
|
return parser.parse_args(args)
|
|
169
194
|
|
|
170
195
|
def init_api(auth_path: Path) -> mijiaAPI:
|
|
171
|
-
class APIUnavailableError(Exception):
|
|
172
|
-
pass
|
|
173
|
-
|
|
174
196
|
if Path(auth_path).is_dir():
|
|
175
197
|
auth_path = auth_path / "auth.json"
|
|
176
|
-
if auth_path.exists():
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if not api.available:
|
|
182
|
-
raise APIUnavailableError()
|
|
183
|
-
return api
|
|
184
|
-
except (json.JSONDecodeError, APIUnavailableError):
|
|
185
|
-
auth_path.unlink(missing_ok=True)
|
|
186
|
-
api = mijiaAPI(auth_data_path=auth_path)
|
|
187
|
-
api.login()
|
|
188
|
-
else:
|
|
198
|
+
if not auth_path.exists():
|
|
199
|
+
print(f"认证文件不存在: {auth_path}")
|
|
200
|
+
print("请调用 'mijiaAPI login' 进行扫描登录")
|
|
201
|
+
sys.exit(1)
|
|
202
|
+
try:
|
|
189
203
|
api = mijiaAPI(auth_data_path=auth_path)
|
|
190
|
-
|
|
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)
|
|
191
217
|
return api
|
|
192
218
|
|
|
193
219
|
def get_homes_list(api: mijiaAPI, verbose: bool = True, device_mapping: Optional[dict] = None) -> dict:
|
|
@@ -316,6 +342,21 @@ def main(args):
|
|
|
316
342
|
hasattr(args, 'func') and args.func is not None):
|
|
317
343
|
return
|
|
318
344
|
|
|
345
|
+
if hasattr(args, 'func') and args.func == 'mcp':
|
|
346
|
+
run_mcp(args.auth_path)
|
|
347
|
+
return
|
|
348
|
+
if hasattr(args, 'func') and args.func == 'login':
|
|
349
|
+
auth_path = args.auth_path
|
|
350
|
+
file_path = Path(auth_path) / "auth.json" if Path(auth_path).is_dir() else Path(auth_path)
|
|
351
|
+
try:
|
|
352
|
+
api = mijiaAPI(auth_data_path=auth_path)
|
|
353
|
+
except json.JSONDecodeError:
|
|
354
|
+
file_path.unlink(missing_ok=True)
|
|
355
|
+
api = mijiaAPI(auth_data_path=auth_path)
|
|
356
|
+
if not api.available:
|
|
357
|
+
api.login()
|
|
358
|
+
return
|
|
359
|
+
|
|
319
360
|
api = init_api(args.auth_path)
|
|
320
361
|
device_mapping = None
|
|
321
362
|
home_mapping = None
|
|
@@ -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]
|