mijiaAPI 1.4.5__tar.gz → 2.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.
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/PKG-INFO +41 -8
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/README.md +40 -7
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/__init__.py +1 -1
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/__main__.py +11 -26
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/apis.py +68 -14
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/devices.py +10 -28
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/login.py +4 -0
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/pyproject.toml +2 -2
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/LICENSE +0 -0
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/code.py +0 -0
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/logger.py +0 -0
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/urls.py +0 -0
- {mijiaapi-1.4.5 → mijiaapi-2.0.0}/mijiaAPI/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mijiaAPI
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: A Python API for Xiaomi Mijia
|
|
5
5
|
License: GPLv3
|
|
6
6
|
Author: Do1e
|
|
@@ -30,6 +30,12 @@ Description-Content-Type: text/markdown
|
|
|
30
30
|
[](https://pypi.org/project/mijiaAPI/)
|
|
31
31
|
[](https://opensource.org/licenses/GPL-3.0)
|
|
32
32
|
|
|
33
|
+
## ⚠️ 重要提醒
|
|
34
|
+
|
|
35
|
+
**自 v1.5.0 版本以来,本项目包含多项破坏性变更!**
|
|
36
|
+
|
|
37
|
+
如果您正在从旧版本升级,请务必查看 [CHANGELOG.md](CHANGELOG.md) 以了解详细的变更内容和迁移指南。
|
|
38
|
+
|
|
33
39
|
## 安装
|
|
34
40
|
|
|
35
41
|
### 从 PyPI 安装(推荐)
|
|
@@ -53,6 +59,13 @@ pip install .
|
|
|
53
59
|
poetry install
|
|
54
60
|
```
|
|
55
61
|
|
|
62
|
+
### aur
|
|
63
|
+
如果你使用 Arch Linux 或基于 Arch 的发行版,可以通过 AUR 安装:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
yay -S python-mijia-api
|
|
67
|
+
```
|
|
68
|
+
|
|
56
69
|
## 使用
|
|
57
70
|
|
|
58
71
|
使用实例可以参考 `demos` 文件夹下的示例代码,以下是基本使用说明。
|
|
@@ -84,15 +97,18 @@ poetry install
|
|
|
84
97
|
|
|
85
98
|
#### 设备与场景获取与控制:
|
|
86
99
|
|
|
100
|
+
下述方法可参考 [demos/test_apis.py](demos/test_apis.py) 中的示例。
|
|
101
|
+
|
|
87
102
|
* `get_devices_list() -> list`:获取设备列表
|
|
88
103
|
* `get_homes_list() -> list`:获取家庭列表(包含房间信息)
|
|
89
104
|
* `get_scenes_list(home_id: str) -> list`:获取手动场景列表
|
|
90
105
|
- 在米家 App 中通过 **米家→添加→手动控制** 设置
|
|
91
106
|
* `run_scene(scene_id: str) -> bool`:运行指定场景
|
|
92
|
-
* `get_consumable_items(home_id: str) -> list
|
|
107
|
+
* `get_consumable_items(home_id: str, owner_id: Optional[int] = None) -> list`:获取设备的耗材信息,如果是共享家庭,需要额外指定 `owner_id` 参数
|
|
93
108
|
* `get_devices_prop(data: list) -> list`:获取设备属性
|
|
94
109
|
* `set_devices_prop(data: list) -> list`:设置设备属性
|
|
95
110
|
* `run_action(data: dict) -> dict`:执行设备的特定动作
|
|
111
|
+
* `get_statistics(data: dict) -> list`:获取设备的统计信息,如空调每个月的耗电量,参考 [demos/test_get_statistics.py](demos/test_get_statistics.py)
|
|
96
112
|
|
|
97
113
|
设备属性和动作的相关参数(`siid`, `piid`, `aiid`)可以从 [米家产品库](https://home.miot-spec.com) 查询:
|
|
98
114
|
* 访问 `https://home.miot-spec.com/spec/{model}`(`model` 在设备列表中获取)
|
|
@@ -115,12 +131,12 @@ device_info = get_device_info('yeelink.light.lamp4') # 米家台灯 1S 的 mode
|
|
|
115
131
|
|
|
116
132
|
### 设备控制封装
|
|
117
133
|
|
|
118
|
-
`
|
|
134
|
+
`mijiaDevice`:基于 `mijiaAPI` 的高级封装,提供更简便的设备控制方式。
|
|
119
135
|
|
|
120
136
|
#### 初始化:
|
|
121
137
|
|
|
122
138
|
```python
|
|
123
|
-
|
|
139
|
+
mijiaDevice(api: mijiaAPI, dev_info: dict = None, dev_name: str = None, did: str = None, sleep_time: float = 0.5)
|
|
124
140
|
```
|
|
125
141
|
|
|
126
142
|
* `api`:已初始化的 `mijiaAPI` 对象
|
|
@@ -138,9 +154,9 @@ mijiaDevices(api: mijiaAPI, dev_info: dict = None, dev_name: str = None, did: st
|
|
|
138
154
|
|
|
139
155
|
#### 使用方法控制:
|
|
140
156
|
|
|
141
|
-
* `set(name: str,
|
|
142
|
-
* `get(name: str, did: str) -> Union[bool, int, float, str]`:获取设备属性
|
|
143
|
-
* `run_action(name: str, did: str = None, value:
|
|
157
|
+
* `set(name: str, value: Union[bool, int, float, str], did: Optional[str] = None) -> bool`:设置设备属性
|
|
158
|
+
* `get(name: str, did: Optional[str] = None) -> Union[bool, int, float, str]`:获取设备属性
|
|
159
|
+
* `run_action(name: str, did: Optional[str] = None, value: Optional[Union[list, tuple]] = None, **kwargs) -> bool`:执行设备动作
|
|
144
160
|
|
|
145
161
|
#### 属性样式访问:
|
|
146
162
|
|
|
@@ -148,7 +164,7 @@ mijiaDevices(api: mijiaAPI, dev_info: dict = None, dev_name: str = None, did: st
|
|
|
148
164
|
|
|
149
165
|
```python
|
|
150
166
|
# 示例:控制台灯
|
|
151
|
-
device =
|
|
167
|
+
device = mijiaDevice(api, dev_name='台灯')
|
|
152
168
|
device.on = True # 打开灯
|
|
153
169
|
device.brightness = 60 # 设置亮度
|
|
154
170
|
current_temp = device.color_temperature # 获取色温
|
|
@@ -248,6 +264,23 @@ mijiaAPI --run 明天天气如何
|
|
|
248
264
|
mijiaAPI --run 打开台灯并将亮度调至最大 --quiet
|
|
249
265
|
```
|
|
250
266
|
|
|
267
|
+
## 常见问题
|
|
268
|
+
|
|
269
|
+
### 账号密码登录失败
|
|
270
|
+
|
|
271
|
+
现在登录似乎100%遇到验证码,建议使用扫码登录。
|
|
272
|
+
|
|
273
|
+
### XXX设备的XXX如何获取/设置
|
|
274
|
+
|
|
275
|
+
我拥有的设备有限,无法保证能解答这类问题,但也欢迎提交 [issue](https://github.com/Do1e/mijia-api/issues),可能需要你将设备共享给我进行抓包或者自行抓包给我提供请求和响应,提供har文件的话注意自行删除cookie等敏感信息。
|
|
276
|
+
|
|
277
|
+
### 如何抓包
|
|
278
|
+
|
|
279
|
+
小米官方给了一个[抓包教程](https://iot.mi.com/new/doc/accesses/direct-access/extension-development/troubleshooting/packet_capture),我没试过,不确定是否能行,如果抓包成功数据是加密的,可以使用 [demos/decrypt.py](demos/decrypt.py) 解密。
|
|
280
|
+
|
|
281
|
+
我自己的解决方案是使用一个获取了root的手机,安装 [reqable](https://reqable.com/zh-CN/) 进行抓包,导出 HAR 文件后使用 [demos/decrypt_har.py](demos/decrypt_har.py) 解密。
|
|
282
|
+
|
|
283
|
+
|
|
251
284
|
## 致谢
|
|
252
285
|
|
|
253
286
|
* [janzlan/mijia-api](https://gitee.com/janzlan/mijia-api/tree/master)
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
[](https://pypi.org/project/mijiaAPI/)
|
|
7
7
|
[](https://opensource.org/licenses/GPL-3.0)
|
|
8
8
|
|
|
9
|
+
## ⚠️ 重要提醒
|
|
10
|
+
|
|
11
|
+
**自 v1.5.0 版本以来,本项目包含多项破坏性变更!**
|
|
12
|
+
|
|
13
|
+
如果您正在从旧版本升级,请务必查看 [CHANGELOG.md](CHANGELOG.md) 以了解详细的变更内容和迁移指南。
|
|
14
|
+
|
|
9
15
|
## 安装
|
|
10
16
|
|
|
11
17
|
### 从 PyPI 安装(推荐)
|
|
@@ -29,6 +35,13 @@ pip install .
|
|
|
29
35
|
poetry install
|
|
30
36
|
```
|
|
31
37
|
|
|
38
|
+
### aur
|
|
39
|
+
如果你使用 Arch Linux 或基于 Arch 的发行版,可以通过 AUR 安装:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
yay -S python-mijia-api
|
|
43
|
+
```
|
|
44
|
+
|
|
32
45
|
## 使用
|
|
33
46
|
|
|
34
47
|
使用实例可以参考 `demos` 文件夹下的示例代码,以下是基本使用说明。
|
|
@@ -60,15 +73,18 @@ poetry install
|
|
|
60
73
|
|
|
61
74
|
#### 设备与场景获取与控制:
|
|
62
75
|
|
|
76
|
+
下述方法可参考 [demos/test_apis.py](demos/test_apis.py) 中的示例。
|
|
77
|
+
|
|
63
78
|
* `get_devices_list() -> list`:获取设备列表
|
|
64
79
|
* `get_homes_list() -> list`:获取家庭列表(包含房间信息)
|
|
65
80
|
* `get_scenes_list(home_id: str) -> list`:获取手动场景列表
|
|
66
81
|
- 在米家 App 中通过 **米家→添加→手动控制** 设置
|
|
67
82
|
* `run_scene(scene_id: str) -> bool`:运行指定场景
|
|
68
|
-
* `get_consumable_items(home_id: str) -> list
|
|
83
|
+
* `get_consumable_items(home_id: str, owner_id: Optional[int] = None) -> list`:获取设备的耗材信息,如果是共享家庭,需要额外指定 `owner_id` 参数
|
|
69
84
|
* `get_devices_prop(data: list) -> list`:获取设备属性
|
|
70
85
|
* `set_devices_prop(data: list) -> list`:设置设备属性
|
|
71
86
|
* `run_action(data: dict) -> dict`:执行设备的特定动作
|
|
87
|
+
* `get_statistics(data: dict) -> list`:获取设备的统计信息,如空调每个月的耗电量,参考 [demos/test_get_statistics.py](demos/test_get_statistics.py)
|
|
72
88
|
|
|
73
89
|
设备属性和动作的相关参数(`siid`, `piid`, `aiid`)可以从 [米家产品库](https://home.miot-spec.com) 查询:
|
|
74
90
|
* 访问 `https://home.miot-spec.com/spec/{model}`(`model` 在设备列表中获取)
|
|
@@ -91,12 +107,12 @@ device_info = get_device_info('yeelink.light.lamp4') # 米家台灯 1S 的 mode
|
|
|
91
107
|
|
|
92
108
|
### 设备控制封装
|
|
93
109
|
|
|
94
|
-
`
|
|
110
|
+
`mijiaDevice`:基于 `mijiaAPI` 的高级封装,提供更简便的设备控制方式。
|
|
95
111
|
|
|
96
112
|
#### 初始化:
|
|
97
113
|
|
|
98
114
|
```python
|
|
99
|
-
|
|
115
|
+
mijiaDevice(api: mijiaAPI, dev_info: dict = None, dev_name: str = None, did: str = None, sleep_time: float = 0.5)
|
|
100
116
|
```
|
|
101
117
|
|
|
102
118
|
* `api`:已初始化的 `mijiaAPI` 对象
|
|
@@ -114,9 +130,9 @@ mijiaDevices(api: mijiaAPI, dev_info: dict = None, dev_name: str = None, did: st
|
|
|
114
130
|
|
|
115
131
|
#### 使用方法控制:
|
|
116
132
|
|
|
117
|
-
* `set(name: str,
|
|
118
|
-
* `get(name: str, did: str) -> Union[bool, int, float, str]`:获取设备属性
|
|
119
|
-
* `run_action(name: str, did: str = None, value:
|
|
133
|
+
* `set(name: str, value: Union[bool, int, float, str], did: Optional[str] = None) -> bool`:设置设备属性
|
|
134
|
+
* `get(name: str, did: Optional[str] = None) -> Union[bool, int, float, str]`:获取设备属性
|
|
135
|
+
* `run_action(name: str, did: Optional[str] = None, value: Optional[Union[list, tuple]] = None, **kwargs) -> bool`:执行设备动作
|
|
120
136
|
|
|
121
137
|
#### 属性样式访问:
|
|
122
138
|
|
|
@@ -124,7 +140,7 @@ mijiaDevices(api: mijiaAPI, dev_info: dict = None, dev_name: str = None, did: st
|
|
|
124
140
|
|
|
125
141
|
```python
|
|
126
142
|
# 示例:控制台灯
|
|
127
|
-
device =
|
|
143
|
+
device = mijiaDevice(api, dev_name='台灯')
|
|
128
144
|
device.on = True # 打开灯
|
|
129
145
|
device.brightness = 60 # 设置亮度
|
|
130
146
|
current_temp = device.color_temperature # 获取色温
|
|
@@ -224,6 +240,23 @@ mijiaAPI --run 明天天气如何
|
|
|
224
240
|
mijiaAPI --run 打开台灯并将亮度调至最大 --quiet
|
|
225
241
|
```
|
|
226
242
|
|
|
243
|
+
## 常见问题
|
|
244
|
+
|
|
245
|
+
### 账号密码登录失败
|
|
246
|
+
|
|
247
|
+
现在登录似乎100%遇到验证码,建议使用扫码登录。
|
|
248
|
+
|
|
249
|
+
### XXX设备的XXX如何获取/设置
|
|
250
|
+
|
|
251
|
+
我拥有的设备有限,无法保证能解答这类问题,但也欢迎提交 [issue](https://github.com/Do1e/mijia-api/issues),可能需要你将设备共享给我进行抓包或者自行抓包给我提供请求和响应,提供har文件的话注意自行删除cookie等敏感信息。
|
|
252
|
+
|
|
253
|
+
### 如何抓包
|
|
254
|
+
|
|
255
|
+
小米官方给了一个[抓包教程](https://iot.mi.com/new/doc/accesses/direct-access/extension-development/troubleshooting/packet_capture),我没试过,不确定是否能行,如果抓包成功数据是加密的,可以使用 [demos/decrypt.py](demos/decrypt.py) 解密。
|
|
256
|
+
|
|
257
|
+
我自己的解决方案是使用一个获取了root的手机,安装 [reqable](https://reqable.com/zh-CN/) 进行抓包,导出 HAR 文件后使用 [demos/decrypt_har.py](demos/decrypt_har.py) 解密。
|
|
258
|
+
|
|
259
|
+
|
|
227
260
|
## 致谢
|
|
228
261
|
|
|
229
262
|
* [janzlan/mijia-api](https://gitee.com/janzlan/mijia-api/tree/master)
|
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
import time
|
|
7
7
|
|
|
8
8
|
from .apis import mijiaAPI
|
|
9
|
-
from .devices import
|
|
9
|
+
from .devices import mijiaDevice, get_device_info
|
|
10
10
|
from .login import mijiaLogin
|
|
11
11
|
|
|
12
12
|
def parse_args(args):
|
|
@@ -146,10 +146,6 @@ def init_api(auth_path: str) -> mijiaAPI:
|
|
|
146
146
|
|
|
147
147
|
def get_devices_list(api: mijiaAPI, verbose: bool = True) -> dict:
|
|
148
148
|
devices = api.get_devices_list()
|
|
149
|
-
if 'list' in devices:
|
|
150
|
-
devices = devices['list']
|
|
151
|
-
else:
|
|
152
|
-
devices = []
|
|
153
149
|
if verbose:
|
|
154
150
|
print("Devices:")
|
|
155
151
|
for device in devices:
|
|
@@ -165,10 +161,6 @@ def get_homes_list(api: mijiaAPI, verbose: bool = True, device_mapping: Optional
|
|
|
165
161
|
if device_mapping is None:
|
|
166
162
|
device_mapping = get_devices_list(api, verbose=False)
|
|
167
163
|
homes = api.get_homes_list()
|
|
168
|
-
if 'homelist' in homes:
|
|
169
|
-
homes = homes['homelist']
|
|
170
|
-
else:
|
|
171
|
-
homes = []
|
|
172
164
|
if verbose:
|
|
173
165
|
print("Homes:")
|
|
174
166
|
for home in homes:
|
|
@@ -189,7 +181,7 @@ def get_homes_list(api: mijiaAPI, verbose: bool = True, device_mapping: Optional
|
|
|
189
181
|
dids = ', '.join(devices_name)
|
|
190
182
|
print(f" - {room['name']}\n"
|
|
191
183
|
f" id: {room['id']}\n"
|
|
192
|
-
f"
|
|
184
|
+
f" devices: {dids}\n"
|
|
193
185
|
f" create time: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(room['create_time']))}")
|
|
194
186
|
home_mapping = {home['id']: home for home in homes}
|
|
195
187
|
return home_mapping
|
|
@@ -200,10 +192,6 @@ def get_scenes_list(api: mijiaAPI, verbose: bool = True, home_mapping: Optional[
|
|
|
200
192
|
scene_mapping = {}
|
|
201
193
|
for home_id, home in home_mapping.items():
|
|
202
194
|
scenes = api.get_scenes_list(home_id)
|
|
203
|
-
if 'scene_info_list' in scenes:
|
|
204
|
-
scenes = scenes['scene_info_list']
|
|
205
|
-
else:
|
|
206
|
-
scenes = []
|
|
207
195
|
if scenes and verbose:
|
|
208
196
|
print(f"Scenes in {home['name']} ({home_id}):")
|
|
209
197
|
for scene in scenes:
|
|
@@ -218,15 +206,12 @@ def get_consumable_items(api: mijiaAPI, home_mapping: Optional[dict] = None):
|
|
|
218
206
|
if home_mapping is None:
|
|
219
207
|
home_mapping = get_homes_list(api, verbose=False)
|
|
220
208
|
for home_id, home in home_mapping.items():
|
|
221
|
-
items = api.get_consumable_items(home_id)
|
|
222
|
-
if 'items' in items:
|
|
223
|
-
items = items['items'][0]['consumes_data']
|
|
224
|
-
else:
|
|
225
|
-
items = []
|
|
209
|
+
items = api.get_consumable_items(home_id, home['uid'])
|
|
226
210
|
print(f"Consumable items in {home['name']} ({home_id}):")
|
|
227
211
|
for item in items:
|
|
228
|
-
|
|
229
|
-
|
|
212
|
+
for consumes_data in item['consumes_data']:
|
|
213
|
+
print(f" - {consumes_data['details'][0]['description']} in {consumes_data['name']}({consumes_data['did']})\n"
|
|
214
|
+
f" value: {consumes_data['details'][0]['value']}")
|
|
230
215
|
|
|
231
216
|
def run_scene(api: mijiaAPI, scene_id: str, scene_mapping: Optional[dict] = None) -> bool:
|
|
232
217
|
if scene_mapping is None:
|
|
@@ -253,15 +238,15 @@ def run_scene(api: mijiaAPI, scene_id: str, scene_mapping: Optional[dict] = None
|
|
|
253
238
|
|
|
254
239
|
def get(args):
|
|
255
240
|
api = init_api(args.auth_path)
|
|
256
|
-
device =
|
|
241
|
+
device = mijiaDevice(api, dev_name=args.dev_name)
|
|
257
242
|
value = device.get(args.prop_name)
|
|
258
243
|
unit = device.prop_list[args.prop_name].unit
|
|
259
244
|
print(f"The {args.prop_name} of {args.dev_name} is {value} {unit if unit else ''}")
|
|
260
245
|
|
|
261
246
|
def set(args):
|
|
262
247
|
api = init_api(args.auth_path)
|
|
263
|
-
device =
|
|
264
|
-
ret = device.
|
|
248
|
+
device = mijiaDevice(api, dev_name=args.dev_name)
|
|
249
|
+
ret = device.set(args.prop_name, args.value)
|
|
265
250
|
unit = device.prop_list[args.prop_name].unit
|
|
266
251
|
if ret:
|
|
267
252
|
print(f"The {args.prop_name} of {args.dev_name} is set to {args.value} {unit if unit else ''}")
|
|
@@ -307,12 +292,12 @@ def main(args):
|
|
|
307
292
|
wifispeaker = None
|
|
308
293
|
for device in device_mapping.values():
|
|
309
294
|
if 'xiaomi.wifispeaker' in device['model']:
|
|
310
|
-
wifispeaker =
|
|
295
|
+
wifispeaker = mijiaDevice(api, dev_name=device['name'])
|
|
311
296
|
break
|
|
312
297
|
if wifispeaker is None:
|
|
313
298
|
raise ValueError("No wifispeaker found")
|
|
314
299
|
else:
|
|
315
|
-
wifispeaker =
|
|
300
|
+
wifispeaker = mijiaDevice(api, dev_name=args.wifispeaker_name)
|
|
316
301
|
wifispeaker.run_action('execute-text-directive', _in=[args.run, args.quiet])
|
|
317
302
|
if hasattr(args, 'func') and args.func is not None:
|
|
318
303
|
if args.func == 'get':
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
|
-
from typing import Union
|
|
2
|
+
from typing import Union, Optional
|
|
3
3
|
|
|
4
4
|
import requests
|
|
5
5
|
import requests.cookies
|
|
@@ -53,16 +53,30 @@ class mijiaAPI(object):
|
|
|
53
53
|
return True
|
|
54
54
|
return False
|
|
55
55
|
|
|
56
|
-
def get_devices_list(self) ->
|
|
56
|
+
def get_devices_list(self) -> list:
|
|
57
57
|
"""
|
|
58
58
|
获取设备列表。
|
|
59
59
|
|
|
60
60
|
Returns:
|
|
61
61
|
dict: 设备列表。
|
|
62
62
|
"""
|
|
63
|
-
uri = '/home/
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
uri = '/home/home_device_list'
|
|
64
|
+
home_list = self.get_homes_list()
|
|
65
|
+
devices = []
|
|
66
|
+
for home in home_list:
|
|
67
|
+
data = {
|
|
68
|
+
"home_owner": home['uid'],
|
|
69
|
+
"home_id": int(home['id']),
|
|
70
|
+
"limit": 200,
|
|
71
|
+
"get_split_device": True,
|
|
72
|
+
"support_smart_home": True,
|
|
73
|
+
"get_cariot_device": True,
|
|
74
|
+
"get_third_device": True
|
|
75
|
+
}
|
|
76
|
+
ret = self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
77
|
+
if ret and ret.get('device_info'):
|
|
78
|
+
devices.extend(ret['device_info'])
|
|
79
|
+
return devices
|
|
66
80
|
|
|
67
81
|
def get_homes_list(self) -> list:
|
|
68
82
|
"""
|
|
@@ -71,9 +85,9 @@ class mijiaAPI(object):
|
|
|
71
85
|
Returns:
|
|
72
86
|
list: 家庭列表,包括房间信息。
|
|
73
87
|
"""
|
|
74
|
-
uri = '/v2/homeroom/
|
|
75
|
-
data = {"fg":
|
|
76
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
88
|
+
uri = '/v2/homeroom/gethome_merged'
|
|
89
|
+
data = {"fg": True, "fetch_share": True, "fetch_share_dev": True, "limit": 300, "app_ver": 7}
|
|
90
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))['homelist']
|
|
77
91
|
|
|
78
92
|
def get_scenes_list(self, home_id: str) -> list:
|
|
79
93
|
"""
|
|
@@ -89,7 +103,10 @@ class mijiaAPI(object):
|
|
|
89
103
|
"""
|
|
90
104
|
uri = '/appgateway/miot/appsceneservice/AppSceneService/GetSceneList'
|
|
91
105
|
data = {"home_id": home_id}
|
|
92
|
-
|
|
106
|
+
ret = self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
107
|
+
if ret and 'scene_info_list' in ret:
|
|
108
|
+
return ret['scene_info_list']
|
|
109
|
+
return []
|
|
93
110
|
|
|
94
111
|
def run_scene(self, scene_id: str) -> bool:
|
|
95
112
|
"""
|
|
@@ -105,19 +122,23 @@ class mijiaAPI(object):
|
|
|
105
122
|
data = {"scene_id": scene_id, "trigger_key": "user.click"}
|
|
106
123
|
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
107
124
|
|
|
108
|
-
def get_consumable_items(self, home_id: str) -> list:
|
|
125
|
+
def get_consumable_items(self, home_id: str, owner_id: Optional[int] = None) -> list:
|
|
109
126
|
"""
|
|
110
127
|
获取耗材列表。
|
|
111
128
|
|
|
112
129
|
Args:
|
|
113
130
|
home_id (str): 家庭ID,从get_homes_list获取。
|
|
131
|
+
owner_id (str, optional): 用户ID,默认为None,如果`home_id`为共享家庭,则需要提供owner_id。
|
|
114
132
|
|
|
115
133
|
Returns:
|
|
116
134
|
list: 耗材列表。
|
|
117
135
|
"""
|
|
118
136
|
uri = '/v2/home/standard_consumable_items'
|
|
119
|
-
data = {"home_id": int(home_id), "owner_id": self.userId}
|
|
120
|
-
|
|
137
|
+
data = {"home_id": int(home_id), "owner_id": int(owner_id) if owner_id else self.userId}
|
|
138
|
+
ret = self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
139
|
+
if ret and 'items' in ret:
|
|
140
|
+
return ret['items']
|
|
141
|
+
return []
|
|
121
142
|
|
|
122
143
|
def get_devices_prop(self, data: list) -> list:
|
|
123
144
|
"""
|
|
@@ -155,7 +176,7 @@ class mijiaAPI(object):
|
|
|
155
176
|
|
|
156
177
|
示例(yeelink.light.lamp4):
|
|
157
178
|
[
|
|
158
|
-
{"did": "1234567890", "siid": 2, "piid": 2, "value": 50}
|
|
179
|
+
{"did": "1234567890", "siid": 2, "piid": 2, "value": 50}, # 设置亮度为50%
|
|
159
180
|
{"did": "1234567890", "siid": 2, "piid": 3, "value": 2700} # 设置色温为2700K
|
|
160
181
|
]
|
|
161
182
|
|
|
@@ -178,7 +199,7 @@ class mijiaAPI(object):
|
|
|
178
199
|
- value: 参数列表
|
|
179
200
|
|
|
180
201
|
示例(xiaomi.feeder.pi2001):
|
|
181
|
-
{"did": "1234567890", "siid": 2, "aiid": 1, "value": [2]}
|
|
202
|
+
{"did": "1234567890", "siid": 2, "aiid": 1, "value": [2]} # 远程喂食2份
|
|
182
203
|
|
|
183
204
|
Returns:
|
|
184
205
|
dict: 操作结果。
|
|
@@ -186,3 +207,36 @@ class mijiaAPI(object):
|
|
|
186
207
|
uri = '/miotspec/action'
|
|
187
208
|
data = {"params": data}
|
|
188
209
|
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
210
|
+
|
|
211
|
+
def get_statistics(self, data: dict) -> list:
|
|
212
|
+
"""
|
|
213
|
+
获取设备的统计信息。
|
|
214
|
+
|
|
215
|
+
Args:
|
|
216
|
+
data (dict): 请求参数,包含以下键:
|
|
217
|
+
- did: 设备ID,从get_devices_list获取
|
|
218
|
+
- key: siid.piid,表示要获取统计数据的属性
|
|
219
|
+
- data_type: 统计类型,可选值包括:
|
|
220
|
+
- 'stat_hour_v3': 按小时统计
|
|
221
|
+
- 'stat_day_v3': 按天统计
|
|
222
|
+
- 'stat_week_v3': 按周统计
|
|
223
|
+
- 'stat_month_v3': 按月统计
|
|
224
|
+
- limit: 返回的最大条目数,可选参数
|
|
225
|
+
- time_start: 开始时间戳,单位为秒
|
|
226
|
+
- time_end: 结束时间戳,单位为秒
|
|
227
|
+
|
|
228
|
+
示例(lumi.acpartner.mcn04 的 power-consumption):
|
|
229
|
+
{
|
|
230
|
+
"did": "1234567890",
|
|
231
|
+
"key": "7.1",
|
|
232
|
+
"data_type": "stat_month_v3",
|
|
233
|
+
"limit": 24,
|
|
234
|
+
"time_start": 1685548800,
|
|
235
|
+
"time_end": 1750694400,
|
|
236
|
+
} # 2023-06-01 00:00:00 到 2025-06-24 00:00:00 的月度统计数据
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
list: 统计信息列表。
|
|
240
|
+
"""
|
|
241
|
+
uri = '/v2/user/statistics'
|
|
242
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
@@ -74,7 +74,7 @@ class DevAction(object):
|
|
|
74
74
|
return f' {self.name}: {self.desc}'
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
class
|
|
77
|
+
class mijiaDevice(object):
|
|
78
78
|
def __init__(
|
|
79
79
|
self,
|
|
80
80
|
api: mijiaAPI,
|
|
@@ -113,7 +113,7 @@ class mijiaDevices(object):
|
|
|
113
113
|
self.api = api
|
|
114
114
|
if dev_info is None:
|
|
115
115
|
devices_list = self.api.get_devices_list()
|
|
116
|
-
matches = [device for device in devices_list
|
|
116
|
+
matches = [device for device in devices_list if device['name'] == dev_name]
|
|
117
117
|
if not matches:
|
|
118
118
|
raise ValueError(f"Device {dev_name} not found")
|
|
119
119
|
elif len(matches) > 1:
|
|
@@ -152,14 +152,14 @@ class mijiaDevices(object):
|
|
|
152
152
|
f"Properties:\n{prop_list_str if prop_list_str else 'No properties available'}\n"
|
|
153
153
|
f"Actions:\n{action_list_str if action_list_str else 'No actions available'}")
|
|
154
154
|
|
|
155
|
-
def set(self, name: str,
|
|
155
|
+
def set(self, name: str, value: Union[bool, int, float, str], did: Optional[str] = None) -> bool:
|
|
156
156
|
"""
|
|
157
157
|
设置设备的属性值。
|
|
158
158
|
|
|
159
159
|
Args:
|
|
160
160
|
name (str): 属性名称。
|
|
161
|
-
did (str): 设备ID。
|
|
162
161
|
value (Union[bool, int, float, str]): 属性值。
|
|
162
|
+
did (str, optional): 设备ID。如未指定,则使用实例化时的did。默认为None。
|
|
163
163
|
|
|
164
164
|
Returns:
|
|
165
165
|
bool: 执行结果(True/False)。
|
|
@@ -168,6 +168,10 @@ class mijiaDevices(object):
|
|
|
168
168
|
ValueError: 如果属性不存在、属性为只读或值无效。
|
|
169
169
|
RuntimeError: 如果设置属性失败。
|
|
170
170
|
"""
|
|
171
|
+
if did is None:
|
|
172
|
+
did = self.did
|
|
173
|
+
if did is None:
|
|
174
|
+
raise ValueError('Please specify the did')
|
|
171
175
|
if name not in self.prop_list:
|
|
172
176
|
raise ValueError(f'Unsupported property: {name}, available properties: {list(self.prop_list.keys())}')
|
|
173
177
|
prop = self.prop_list[name]
|
|
@@ -232,28 +236,6 @@ class mijiaDevices(object):
|
|
|
232
236
|
logger.debug(f"Set property: {self.name} -> {name}, value: {value}, result: {result}")
|
|
233
237
|
return result['code'] == 0
|
|
234
238
|
|
|
235
|
-
def set_v2(self, name: str, value: Union[bool, int, float, str], did: Optional[str] = None) -> bool:
|
|
236
|
-
"""
|
|
237
|
-
设置设备的属性值(v2版本,需在实例化时指定did或在调用时提供)。
|
|
238
|
-
|
|
239
|
-
Args:
|
|
240
|
-
name (str): 属性名称。
|
|
241
|
-
value (Union[bool, int, float, str]): 属性值。
|
|
242
|
-
did (str, optional): 设备ID。如未指定,则使用实例化时的did。默认为None。
|
|
243
|
-
|
|
244
|
-
Returns:
|
|
245
|
-
bool: 执行结果(True/False)。
|
|
246
|
-
|
|
247
|
-
Raises:
|
|
248
|
-
ValueError: 如果未指定设备ID。
|
|
249
|
-
"""
|
|
250
|
-
if did is not None:
|
|
251
|
-
return self.set(name, did, value)
|
|
252
|
-
elif self.did is not None:
|
|
253
|
-
return self.set(name, self.did, value)
|
|
254
|
-
else:
|
|
255
|
-
raise ValueError('Please specify the did')
|
|
256
|
-
|
|
257
239
|
def get(self, name: str, did: Optional[str] = None) -> Union[bool, int, float, str]:
|
|
258
240
|
"""
|
|
259
241
|
获取设备的属性值。
|
|
@@ -303,7 +285,7 @@ class mijiaDevices(object):
|
|
|
303
285
|
RuntimeError: 如果设置属性失败。
|
|
304
286
|
"""
|
|
305
287
|
if 'prop_list' in self.__dict__ and name in self.prop_list:
|
|
306
|
-
if not self.
|
|
288
|
+
if not self.set(name, value):
|
|
307
289
|
raise RuntimeError(f'Failed to set property: {name}')
|
|
308
290
|
else:
|
|
309
291
|
super().__setattr__(name, value)
|
|
@@ -378,7 +360,7 @@ class mijiaDevices(object):
|
|
|
378
360
|
|
|
379
361
|
def get_device_info(device_model: str, cache_path: Optional[str] = os.path.join(os.path.expanduser("~"), ".config/mijia-api")) -> dict:
|
|
380
362
|
"""
|
|
381
|
-
获取设备信息,用于初始化
|
|
363
|
+
获取设备信息,用于初始化mijiaDevice对象。
|
|
382
364
|
|
|
383
365
|
Args:
|
|
384
366
|
device_model (str): 设备型号,从get_devices_list获取。
|
|
@@ -122,6 +122,8 @@ class mijiaLogin(object):
|
|
|
122
122
|
latest_utc_time = max(parsed_times)
|
|
123
123
|
china_time = latest_utc_time + timedelta(hours=8)
|
|
124
124
|
|
|
125
|
+
# [FIXME] 实测此处的过期时间并不准确,实际过期时间可能大于此处获取的时间
|
|
126
|
+
# cookie 中唯一用到的 serviceToken 并无过期时间
|
|
125
127
|
return china_time
|
|
126
128
|
|
|
127
129
|
def _save_auth(self) -> None:
|
|
@@ -188,6 +190,7 @@ class mijiaLogin(object):
|
|
|
188
190
|
'ssecurity': ret_data['ssecurity'],
|
|
189
191
|
'deviceId': data['deviceId'],
|
|
190
192
|
'serviceToken': cookies['serviceToken'],
|
|
193
|
+
'cUserId': cookies['cUserId'],
|
|
191
194
|
'expireTime': self._extract_latest_gmt_datetime(cookies).strftime('%Y-%m-%d %H:%M:%S'),
|
|
192
195
|
'account_info': self._get_account_info(ret_data['userId'])
|
|
193
196
|
}
|
|
@@ -273,6 +276,7 @@ class mijiaLogin(object):
|
|
|
273
276
|
'ssecurity': ret_data['ssecurity'],
|
|
274
277
|
'deviceId': data['deviceId'],
|
|
275
278
|
'serviceToken': cookies['serviceToken'],
|
|
279
|
+
'cUserId': cookies['cUserId'],
|
|
276
280
|
'expireTime': self._extract_latest_gmt_datetime(cookies).strftime('%Y-%m-%d %H:%M:%S'),
|
|
277
281
|
'account_info': self._get_account_info(ret_data['userId'])
|
|
278
282
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mijiaAPI"
|
|
3
|
-
version = "
|
|
3
|
+
version = "2.0.0"
|
|
4
4
|
description = "A Python API for Xiaomi Mijia"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.9,<4.0"
|
|
@@ -15,7 +15,7 @@ mijiaAPI = "mijiaAPI.__main__:cli"
|
|
|
15
15
|
|
|
16
16
|
[tool.poetry]
|
|
17
17
|
name = "mijiaAPI"
|
|
18
|
-
version = "
|
|
18
|
+
version = "2.0.0"
|
|
19
19
|
description = "A Python API for Xiaomi Mijia"
|
|
20
20
|
authors = ["Do1e <dpj.email@qq.com>"]
|
|
21
21
|
license = "GPLv3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|