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.
Files changed (33) hide show
  1. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/.gitignore +4 -0
  2. mijiaapi-4.1.0/PKG-INFO +107 -0
  3. mijiaapi-4.1.0/README.md +86 -0
  4. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/__main__.py +58 -17
  5. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/apis.py +37 -7
  6. mijiaapi-4.1.0/mijiaAPI/mcp_server.py +405 -0
  7. mijiaapi-4.1.0/mijiaAPI/version.py +1 -0
  8. mijiaapi-4.1.0/mijiaAPI.egg-info/PKG-INFO +107 -0
  9. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/SOURCES.txt +1 -2
  10. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/requires.txt +1 -0
  11. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/pyproject.toml +2 -1
  12. mijiaapi-4.1.0/uv.lock +2139 -0
  13. mijiaapi-3.2.0/CHANGELOG.md +0 -161
  14. mijiaapi-3.2.0/FAQ.md +0 -24
  15. mijiaapi-3.2.0/PKG-INFO +0 -596
  16. mijiaapi-3.2.0/README.md +0 -576
  17. mijiaapi-3.2.0/mijiaAPI/version.py +0 -1
  18. mijiaapi-3.2.0/mijiaAPI.egg-info/PKG-INFO +0 -596
  19. mijiaapi-3.2.0/uv.lock +0 -749
  20. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  21. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/LICENSE +0 -0
  22. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/demos/test_apis.py +0 -0
  23. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/demos/test_get_statistics.py +0 -0
  24. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/demos/test_login.py +0 -0
  25. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/__init__.py +0 -0
  26. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/devices.py +0 -0
  27. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/errors.py +0 -0
  28. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/logger.py +0 -0
  29. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI/miutils.py +0 -0
  30. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/dependency_links.txt +0 -0
  31. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/entry_points.txt +0 -0
  32. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/mijiaAPI.egg-info/top_level.txt +0 -0
  33. {mijiaapi-3.2.0 → mijiaapi-4.1.0}/setup.cfg +0 -0
@@ -17,3 +17,7 @@ wheels/
17
17
 
18
18
  # har files
19
19
  *.har
20
+
21
+ # VitePress
22
+ node_modules/
23
+ /.vitepress/
@@ -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
+ [![GitHub](https://img.shields.io/badge/GitHub-Do1e%2Fmijia--api-blue)](https://github.com/Do1e/mijia-api)
29
+ [![PyPI](https://img.shields.io/badge/PyPI-mijiaAPI-blue)](https://pypi.org/project/mijiaAPI/)
30
+ [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-green.svg)](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
+ * 开发者不对使用本项目产生的任何直接或间接损失负责
@@ -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
+ [![GitHub](https://img.shields.io/badge/GitHub-Do1e%2Fmijia--api-blue)](https://github.com/Do1e/mijia-api)
8
+ [![PyPI](https://img.shields.io/badge/PyPI-mijiaAPI-blue)](https://pypi.org/project/mijiaAPI/)
9
+ [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-green.svg)](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
- try:
178
- api = mijiaAPI(auth_data_path=auth_path)
179
- if not api.available:
180
- api._refresh_token()
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
- api.login()
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
- # Step 1: 从 serviceLogin 获取登录链接参数
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 self.auth_data
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
- self._print_qr(login_data["loginUrl"])
255
- print(f"也可以访问链接查看二维码图片: {login_data['qr']}")
269
+ return login_data
270
+
271
+ def _complete_qr_login(self, login_data: dict) -> dict:
272
+ """长轮询等待扫码并完成登录,保存认证数据。
256
273
 
257
- # Step 3: 轮询等待扫码登录
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]