mijiaAPI 1.3.10__tar.gz → 1.3.12__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mijiaAPI
3
- Version: 1.3.10
3
+ Version: 1.3.12
4
4
  Summary: A Python API for Xiaomi Mijia
5
5
  License: GPLv3
6
6
  Author: Do1e
@@ -0,0 +1,27 @@
1
+ import argparse
2
+ import sys
3
+
4
+ from .login import mijiaLogin
5
+
6
+ def parse_args(args):
7
+ parser = argparse.ArgumentParser(description="Mijia API CLI")
8
+ parser.add_argument(
9
+ '-i', '--login',
10
+ action='store_true',
11
+ help="Login by QR code and save cookies to file",
12
+ )
13
+ parser.add_argument(
14
+ '-c', '--cookie_path',
15
+ type=str,
16
+ help="Path to the cookies file",
17
+ )
18
+ return parser.parse_args(args)
19
+
20
+ def main(args):
21
+ args = parse_args(args)
22
+ if args.login:
23
+ api = mijiaLogin(save_path=args.cookie_path)
24
+ auth = api.QRlogin()
25
+
26
+ if __name__ == "__main__":
27
+ main(sys.argv[1:])
@@ -0,0 +1,186 @@
1
+ from typing import Union
2
+ import requests
3
+ import requests.cookies
4
+
5
+ from .utils import defaultUA, post_data, PostDataError
6
+
7
+
8
+ class mijiaAPI(object):
9
+ def __init__(self, auth_data: dict):
10
+ """
11
+ 初始化mijiaAPI对象。
12
+
13
+ Args:
14
+ auth_data (dict): 包含授权信息的字典,必须包含'userId'、'deviceId'、'ssecurity'和'serviceToken'。
15
+
16
+ Raises:
17
+ Exception: 当授权数据不完整时抛出异常。
18
+ """
19
+ if any(k not in auth_data for k in ['userId', 'deviceId', 'ssecurity', 'serviceToken']):
20
+ raise Exception('Invalid authorize data')
21
+ self.userId = auth_data['userId']
22
+ self.ssecurity = auth_data['ssecurity']
23
+ self.session = requests.Session()
24
+ self.session.headers.update({
25
+ 'User-Agent': defaultUA,
26
+ 'x-xiaomi-protocal-flag-cli': 'PROTOCAL-HTTP2',
27
+ 'Cookie': f'PassportDeviceId={auth_data["deviceId"]};'
28
+ f'userId={auth_data["userId"]};'
29
+ f'serviceToken={auth_data["serviceToken"]};',
30
+ })
31
+
32
+ @staticmethod
33
+ def _post_process(data: dict) -> Union[list, bool]:
34
+ if data['code'] != 0:
35
+ raise Exception(f'Failed to get data, {data["message"]}')
36
+ return data['result']
37
+
38
+ @property
39
+ def available(self) -> bool:
40
+ """
41
+ 检查API是否可用。
42
+
43
+ Returns:
44
+ bool: API可用返回True,否则返回False。
45
+ """
46
+ uri = '/home/device_list'
47
+ data = {"getVirtualModel": False, "getHuamiDevices": 0}
48
+ try:
49
+ post_data(self.session, self.ssecurity, uri, data)
50
+ return True
51
+ except PostDataError:
52
+ return False
53
+
54
+ def get_devices_list(self) -> dict:
55
+ """
56
+ 获取设备列表。
57
+
58
+ Returns:
59
+ dict: 设备列表。
60
+ """
61
+ uri = '/home/device_list'
62
+ data = {"getVirtualModel": False, "getHuamiDevices": 0}
63
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
64
+
65
+ def get_homes_list(self) -> list:
66
+ """
67
+ 获取家庭列表。
68
+
69
+ Returns:
70
+ list: 家庭列表,包括房间信息。
71
+ """
72
+ uri = '/v2/homeroom/gethome'
73
+ data = {"fg": False, "fetch_share": True, "fetch_share_dev": True, "limit": 300, "app_ver": 7}
74
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
75
+
76
+ def get_scenes_list(self, home_id: str) -> list:
77
+ """
78
+ 获取场景列表。
79
+
80
+ 在米家APP中通过"添加 -> 手动控制"设置。
81
+
82
+ Args:
83
+ home_id (str): 家庭ID,从get_homes_list获取。
84
+
85
+ Returns:
86
+ list: 场景列表。
87
+ """
88
+ uri = '/appgateway/miot/appsceneservice/AppSceneService/GetSceneList'
89
+ data = {"home_id": home_id}
90
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
91
+
92
+ def run_scene(self, scene_id: str) -> bool:
93
+ """
94
+ 运行场景。
95
+
96
+ Args:
97
+ scene_id (str): 场景ID,从get_scenes_list获取。
98
+
99
+ Returns:
100
+ bool: 操作结果。
101
+ """
102
+ uri = '/appgateway/miot/appsceneservice/AppSceneService/RunScene'
103
+ data = {"scene_id": scene_id, "trigger_key": "user.click"}
104
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
105
+
106
+ def get_consumable_items(self, home_id: str) -> list:
107
+ """
108
+ 获取耗材列表。
109
+
110
+ Args:
111
+ home_id (str): 家庭ID,从get_homes_list获取。
112
+
113
+ Returns:
114
+ list: 耗材列表。
115
+ """
116
+ uri = '/v2/home/standard_consumable_items'
117
+ data = {"home_id": int(home_id), "owner_id": self.userId}
118
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
119
+
120
+ def get_devices_prop(self, data: list) -> list:
121
+ """
122
+ 获取设备属性。
123
+
124
+ Args:
125
+ data (list): 设备属性请求列表,每项为包含以下键的字典:
126
+ - did: 设备ID,从get_devices_list获取
127
+ - siid: 服务ID,从 https://home.miot-spec.com/spec/{model} 获取,model从get_devices_list获取
128
+ - piid: 属性ID,从 https://home.miot-spec.com/spec/{model} 获取,model从get_devices_list获取
129
+
130
+ 示例(yeelink.light.lamp4):
131
+ [
132
+ {"did": "1234567890", "siid": 2, "piid": 2}, # 获取亮度
133
+ {"did": "1234567890", "siid": 2, "piid": 3}, # 获取色温
134
+ ]
135
+
136
+ Returns:
137
+ list: 设备属性信息列表。
138
+ """
139
+ uri = '/miotspec/prop/get'
140
+ data = {"params": data}
141
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
142
+
143
+ def set_devices_prop(self, data: list) -> list:
144
+ """
145
+ 设置设备属性。
146
+
147
+ Args:
148
+ data (list): 设备属性设置列表,每项为包含以下键的字典:
149
+ - did: 设备ID,从get_devices_list获取
150
+ - siid: 服务ID,从 https://home.miot-spec.com/spec/{model} 获取,model从get_devices_list获取
151
+ - piid: 属性ID,从 https://home.miot-spec.com/spec/{model} 获取,model从get_devices_list获取
152
+ - value: 要设置的值
153
+
154
+ 示例(yeelink.light.lamp4):
155
+ [
156
+ {"did": "1234567890", "siid": 2, "piid": 2, "value": 50} # 设置亮度为50%
157
+ {"did": "1234567890", "siid": 2, "piid": 3, "value": 2700} # 设置色温为2700K
158
+ ]
159
+
160
+ Returns:
161
+ list: 操作结果。
162
+ """
163
+ uri = '/miotspec/prop/set'
164
+ data = {"params": data}
165
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
166
+
167
+ def run_action(self, data: dict) -> dict:
168
+ """
169
+ 执行设备动作。
170
+
171
+ Args:
172
+ data (dict): 动作请求,包含以下键:
173
+ - did: 设备ID,从get_devices_list获取
174
+ - siid: 服务ID,从 https://home.miot-spec.com/spec/{model} 获取,model从get_devices_list获取
175
+ - aiid: 动作ID,从 https://home.miot-spec.com/spec/{model} 获取,model从get_devices_list获取
176
+ - value: 参数列表
177
+
178
+ 示例(xiaomi.feeder.pi2001):
179
+ {"did": "1234567890", "siid": 2, "aiid": 1, "value": [2]}, # 远程喂食2份
180
+
181
+ Returns:
182
+ dict: 操作结果。
183
+ """
184
+ uri = '/miotspec/action'
185
+ data = {"params": data}
186
+ return self._post_process(post_data(self.session, self.ssecurity, uri, data))
@@ -11,8 +11,13 @@ from .logger import logger
11
11
  class DevProp(object):
12
12
  def __init__(self, prop_dict: dict):
13
13
  """
14
- Initialize the property object 初始化属性对象
15
- :param prop_dict:
14
+ 初始化属性对象。
15
+
16
+ Args:
17
+ prop_dict (dict): 属性字典。
18
+
19
+ Raises:
20
+ ValueError: 如果属性类型不受支持。
16
21
  """
17
22
  self.name = prop_dict['name']
18
23
  self.desc = prop_dict['description']
@@ -27,8 +32,10 @@ class DevProp(object):
27
32
 
28
33
  def __str__(self):
29
34
  """
30
- String representation of the property 返回属性的名称、描述、类型、读写权限、单位和范围
31
- :return:
35
+ 返回属性的字符串表示。
36
+
37
+ Returns:
38
+ str: 属性的名称、描述、类型、读写权限、单位和范围。
32
39
  """
33
40
  lines = [
34
41
  f" {self.name}: {self.desc}",
@@ -44,11 +51,23 @@ class DevProp(object):
44
51
 
45
52
  class DevAction(object):
46
53
  def __init__(self, act_dict: dict):
54
+ """
55
+ 初始化动作对象。
56
+
57
+ Args:
58
+ act_dict (dict): 动作字典。
59
+ """
47
60
  self.name = act_dict['name']
48
61
  self.desc = act_dict['description']
49
62
  self.method = act_dict['method']
50
63
 
51
64
  def __str__(self):
65
+ """
66
+ 返回动作的字符串表示。
67
+
68
+ Returns:
69
+ str: 动作的名称和描述。
70
+ """
52
71
  return f' {self.name}: {self.desc}'
53
72
 
54
73
 
@@ -62,16 +81,29 @@ class mijiaDevices(object):
62
81
  sleep_time: Optional[Union[int, float]] = 0.5
63
82
  ):
64
83
  """
65
- Initialize the device object 初始化设备对象
66
- 若未提供设备信息,则根据设备名称获取设备信息,若两者均未提供,则抛出异常
67
- 若提供了设备信息的同时提供了设备名称,则以设备信息为准
68
- :param api: 米家API对象
69
- :param dev_info: 设备信息字典(可选)
70
- :param dev_name: 设备名称(可选)
71
- :param did: 设备ID(可选)
72
- :param sleep_time: 调用设备属性的间隔时间(可选,默认0.5秒)
84
+ 初始化设备对象。
85
+
86
+ 如果未提供设备信息,则根据设备名称获取设备信息。如果两者均未提供,则抛出异常。
87
+ 如果同时提供了设备信息和设备名称,则以设备信息为准。
88
+
89
+ Args:
90
+ api (mijiaAPI): 米家API对象。
91
+ dev_info (dict, optional): 设备信息字典,从get_device_info获取。默认为None。
92
+ dev_name (str, optional): 设备名称,从get_devices_list获取。默认为None。
93
+ did (str, optional): 设备ID,如未指定,则需要在调用get/set时指定。默认为None。
94
+ sleep_time ([int, float], optional): 调用设备属性的间隔时间。默认为0.5秒。
95
+
96
+ Raises:
97
+ RuntimeError: 如果dev_info和dev_name都未提供。
98
+ ValueError: 如果找不到指定设备或找到多个同名设备。
99
+
100
+ Note:
101
+ - 如果同时提供了dev_info和dev_name,则以dev_info为准。
102
+ - 如果只提供了dev_name,则根据名称自动获取设备信息。
103
+ - 如果只提供了dev_info,则直接使用该信息。
73
104
  """
74
- assert dev_info is not None or dev_name is not None, "Either 'dev_info' or 'dev_name' must be provided."
105
+ if dev_info is None and dev_name is None:
106
+ raise RuntimeError("Either 'dev_info' or 'dev_name' must be provided.")
75
107
  if dev_info is not None and dev_name is not None:
76
108
  logger.warning("Both 'dev_info' and 'dev_name' provided. Using 'dev_info' for initialization.")
77
109
 
@@ -106,8 +138,10 @@ class mijiaDevices(object):
106
138
 
107
139
  def __str__(self) -> str:
108
140
  """
109
- String representation of the device 返回设备的属性和动作列表
110
- :return: 设备的属性和动作列表
141
+ 返回设备的字符串表示。
142
+
143
+ Returns:
144
+ str: 设备的属性和动作列表。
111
145
  """
112
146
  prop_list_str = '\n'.join(filter(None, (str(v) for k, v in self.prop_list.items() if '_' not in k)))
113
147
  action_list_str = '\n'.join(map(str, self.action_list.values()))
@@ -115,13 +149,21 @@ class mijiaDevices(object):
115
149
  f"Properties:\n{prop_list_str if prop_list_str else 'No properties available'}\n"
116
150
  f"Actions:\n{action_list_str if action_list_str else 'No actions available'}")
117
151
 
118
- def set(self, name: str, did: str, value: Union[bool, int]) -> Union[bool, int]:
152
+ def set(self, name: str, did: str, value: Union[bool, int, float, str]) -> bool:
119
153
  """
120
- Set property value 设置设备的属性值
121
- :param name: 属性名称
122
- :param did: 设备ID
123
- :param value: 属性值
124
- :return: 执行结果(True/False)
154
+ 设置设备的属性值。
155
+
156
+ Args:
157
+ name (str): 属性名称。
158
+ did (str): 设备ID。
159
+ value (Union[bool, int, float, str]): 属性值。
160
+
161
+ Returns:
162
+ bool: 执行结果(True/False)。
163
+
164
+ Raises:
165
+ ValueError: 如果属性不存在、属性为只读或值无效。
166
+ RuntimeError: 如果设置属性失败。
125
167
  """
126
168
  if name not in self.prop_list:
127
169
  raise ValueError(f'Unsupported property: {name}, available properties: {list(self.prop_list.keys())}')
@@ -170,13 +212,20 @@ class mijiaDevices(object):
170
212
  sleep(self.sleep_time)
171
213
  return result['code'] == 0
172
214
 
173
- def set_v2(self, name: str, value: Union[bool, int], did: Optional[str] = None) -> Union[bool, int]:
215
+ def set_v2(self, name: str, value: Union[bool, int, float, str], did: Optional[str] = None) -> bool:
174
216
  """
175
- Set property value_v2 设置设备的属性值_v2(需在实例化时指定did
176
- :param name: 属性名称
177
- :param value: 属性值
178
- :param did: 设备ID(若未指定,则使用实例化时的did)
179
- :return: 执行结果(True/False)
217
+ 设置设备的属性值(v2版本,需在实例化时指定did或在调用时提供)。
218
+
219
+ Args:
220
+ name (str): 属性名称。
221
+ value (Union[bool, int, float, str]): 属性值。
222
+ did (str, optional): 设备ID。如未指定,则使用实例化时的did。默认为None。
223
+
224
+ Returns:
225
+ bool: 执行结果(True/False)。
226
+
227
+ Raises:
228
+ ValueError: 如果未指定设备ID。
180
229
  """
181
230
  if did is not None:
182
231
  return self.set(name, did, value)
@@ -185,12 +234,20 @@ class mijiaDevices(object):
185
234
  else:
186
235
  raise ValueError('Please specify the did')
187
236
 
188
- def get(self, name: str, did: Optional[str] = None) -> Union[bool, int]:
237
+ def get(self, name: str, did: Optional[str] = None) -> Union[bool, int, float, str]:
189
238
  """
190
- Get property value 获取设备的属性值
191
- :param name: 属性名称
192
- :param did: 设备ID(若未指定,则使用实例化时的did)
193
- :return: 属性值
239
+ 获取设备的属性值。
240
+
241
+ Args:
242
+ name (str): 属性名称。
243
+ did (str, optional): 设备ID。如未指定,则使用实例化时的did。默认为None。
244
+
245
+ Returns:
246
+ Union[bool, int, float, str]: 属性值。
247
+
248
+ Raises:
249
+ ValueError: 如果未指定设备ID、属性不存在或属性为只写。
250
+ RuntimeError: 如果获取属性失败。
194
251
  """
195
252
  if did is None:
196
253
  did = self.did
@@ -213,12 +270,16 @@ class mijiaDevices(object):
213
270
  sleep(self.sleep_time)
214
271
  return result['value']
215
272
 
216
- def __setattr__(self, name: str, value: Union[bool, int]) -> None:
273
+ def __setattr__(self, name: str, value: Union[bool, int, float, str]) -> None:
217
274
  """
218
- Set property value 设置设备的属性值(需在实例化时指定did
219
- :param name: 属性名称
220
- :param value: 属性值
221
- :return: None
275
+ 设置设备的属性值(通过对象属性方式,需在实例化时指定did)。
276
+
277
+ Args:
278
+ name (str): 属性名称。
279
+ value (Union[bool, int, float, str]): 属性值。
280
+
281
+ Raises:
282
+ RuntimeError: 如果设置属性失败。
222
283
  """
223
284
  if 'prop_list' in self.__dict__ and name in self.prop_list:
224
285
  if not self.set_v2(name, value):
@@ -226,11 +287,15 @@ class mijiaDevices(object):
226
287
  else:
227
288
  super().__setattr__(name, value)
228
289
 
229
- def __getattr__(self, name: str) -> Union[bool, int]:
290
+ def __getattr__(self, name: str) -> Union[bool, int, float, str]:
230
291
  """
231
- Get property value 获取设备的属性值(需在实例化时指定did
232
- :param name: 属性名称
233
- :return: 属性值
292
+ 获取设备的属性值(通过对象属性方式,需在实例化时指定did)。
293
+
294
+ Args:
295
+ name (str): 属性名称。
296
+
297
+ Returns:
298
+ Union[bool, int, float, str]: 属性值。
234
299
  """
235
300
  if 'prop_list' in self.__dict__ and name in self.prop_list:
236
301
  return self.get(name)
@@ -245,12 +310,20 @@ class mijiaDevices(object):
245
310
  **kwargs
246
311
  ) -> bool:
247
312
  """
248
- Run action 运行设备动作
249
- :param name: 动作名称
250
- :param did: 设备ID(若未指定,则使用实例化时的did)
251
- :param value: 动作参数(若动作不需要参数,则不需要指定)
252
- :param kwargs: 其它动作参数(若动作不需要参数,则不需要指定)
253
- :return: 执行结果(True/False)
313
+ 运行设备动作。
314
+
315
+ Args:
316
+ name (str): 动作名称。
317
+ did (Optional[str], optional): 设备ID。如未指定,则使用实例化时的did。默认为None。
318
+ value (Optional[Union[list, tuple]], optional): 动作参数。如动作不需要参数,则不需指定。默认为None。
319
+ **kwargs: 其它动作参数,如智能音箱的in参数[run_action('execute-text-directive', _in=['空调调至26度', True])]。
320
+
321
+ Returns:
322
+ bool: 执行结果(True/False)。
323
+
324
+ Raises:
325
+ ValueError: 如果未指定设备ID、动作不存在或参数无效。
326
+ RuntimeError: 如果运行动作失败。
254
327
  """
255
328
  if did is None:
256
329
  did = self.did
@@ -283,9 +356,16 @@ class mijiaDevices(object):
283
356
 
284
357
  def get_device_info(device_model: str) -> dict:
285
358
  """
286
- Get device info 获取设备信息
287
- :param device_model: 设备型号
288
- :return: 设备信息字典
359
+ 获取设备信息,用于初始化mijiaDevices对象。
360
+
361
+ Args:
362
+ device_model (str): 设备型号,从get_devices_list获取。
363
+
364
+ Returns:
365
+ dict: 设备信息字典。
366
+
367
+ Raises:
368
+ RuntimeError: 如果获取设备信息失败。
289
369
  """
290
370
  response = requests.get(deviceURL + device_model)
291
371
  if response.status_code != 200:
@@ -1,9 +1,9 @@
1
+ from typing import Optional
1
2
  import hashlib
2
3
  import json
3
4
  import os
4
5
  import random
5
6
  import string
6
- import sys
7
7
  import time
8
8
  from urllib import parse
9
9
 
@@ -17,15 +17,27 @@ from .logger import logger
17
17
 
18
18
  class LoginError(Exception):
19
19
  def __init__(self, code: int, message: str):
20
+ """
21
+ 初始化登录错误异常。
22
+
23
+ Args:
24
+ code (int): 错误代码。
25
+ message (str): 错误消息。
26
+ """
20
27
  self.code = code
21
28
  self.message = message
22
29
  super().__init__(f'Error code: {code}, message: {message}')
23
30
 
24
31
 
25
32
  class mijiaLogin(object):
26
- def __init__(self, save_auth=True, save_path='jsons/auth.json'):
33
+ def __init__(self, save_path: Optional[str] = None):
34
+ """
35
+ 初始化米家登录对象。
36
+
37
+ Args:
38
+ save_path (str, optional): 认证数据保存路径。默认为None。
39
+ """
27
40
  self.auth_data = None
28
- self.save_auth = save_auth
29
41
  self.save_path = save_path
30
42
 
31
43
  self.deviceId = ''.join(random.sample(string.digits + string.ascii_letters, 16))
@@ -39,6 +51,15 @@ class mijiaLogin(object):
39
51
  })
40
52
 
41
53
  def _get_index(self) -> dict[str, str]:
54
+ """
55
+ 获取索引页数据。
56
+
57
+ Returns:
58
+ dict[str, str]: 包含设备ID和其他必要参数的字典。
59
+
60
+ Raises:
61
+ LoginError: 请求索引页失败时抛出。
62
+ """
42
63
  ret = self.session.get(msgURL)
43
64
  if ret.status_code != 200:
44
65
  raise LoginError(ret.status_code, f'Failed to get index page, {ret.text}')
@@ -51,6 +72,18 @@ class mijiaLogin(object):
51
72
  return data
52
73
 
53
74
  def _get_account(self, user_id: str) -> dict[str, str]:
75
+ """
76
+ 获取账户信息。
77
+
78
+ Args:
79
+ user_id (str): 用户ID。
80
+
81
+ Returns:
82
+ dict[str, str]: 包含账户信息的字典。
83
+
84
+ Raises:
85
+ LoginError: 获取账户信息失败时抛出。
86
+ """
54
87
  ret = self.session.get(accountURL + str(user_id))
55
88
  if ret.status_code != 200:
56
89
  raise LoginError(ret.status_code, f'Failed to get account page, {ret.text}')
@@ -62,9 +95,18 @@ class mijiaLogin(object):
62
95
  return data
63
96
 
64
97
  def _save_auth(self) -> None:
65
- if self.save_auth and self.auth_data is not None:
98
+ """
99
+ 保存认证数据到文件。
100
+
101
+ 如果设置了保存路径且有认证数据,则将其以JSON格式保存到指定路径。
102
+ """
103
+ if self.save_path is not None and self.auth_data is not None:
66
104
  if not os.path.isabs(self.save_path):
67
105
  self.save_path = os.path.abspath(self.save_path)
106
+ if os.path.exists(self.save_path) and not os.path.isfile(self.save_path):
107
+ raise ValueError(f'Path [{self.save_path}] is not a file')
108
+ if not os.path.exists(os.path.dirname(self.save_path)):
109
+ os.makedirs(os.path.dirname(self.save_path))
68
110
  with open(self.save_path, 'w') as f:
69
111
  json.dump(self.auth_data, f, indent=2)
70
112
  logger.info(f'Auth data saved to [{self.save_path}]')
@@ -72,15 +114,18 @@ class mijiaLogin(object):
72
114
  logger.info('Auth data not saved')
73
115
 
74
116
  def login(self, username: str, password: str) -> dict:
75
- """login with username and password
76
- mijiaLogin.login(username: str, password: str) -> dict
77
- -------
78
- @param
79
- username: str, xiaomi account username(email/phone number/xiaomi id)
80
- password: str, xiaomi account password
81
- -------
82
- @return
83
- dict, data for authorization, including userId, ssecurity, deviceId, serviceToken
117
+ """
118
+ 使用用户名和密码登录。
119
+
120
+ Args:
121
+ username (str): 小米账户用户名(邮箱/手机号/小米ID)。
122
+ password (str): 小米账户密码。
123
+
124
+ Returns:
125
+ dict: 授权数据,包含userIdssecuritydeviceIdserviceToken
126
+
127
+ Raises:
128
+ LoginError: 登录失败时抛出。
84
129
  """
85
130
  logger.warning('There is a high probability of verification code with account and password. Please try other login methods')
86
131
  data = self._get_index()
@@ -116,12 +161,18 @@ class mijiaLogin(object):
116
161
  self.auth_data = auth_data
117
162
  self.auth_data.update(self._get_account(auth_data['userId']))
118
163
 
119
- if self.save_auth:
120
- self._save_auth()
164
+ self._save_auth()
121
165
  return auth_data
122
166
 
123
167
  @staticmethod
124
168
  def _print_qr(loginurl: str, box_size: int = 10) -> None:
169
+ """
170
+ 打印并保存二维码。
171
+
172
+ Args:
173
+ loginurl (str): 包含登录信息的URL。
174
+ box_size (int, optional): 二维码大小。默认为10。
175
+ """
125
176
  logger.info('Scan the QR code below with Mi Home app')
126
177
  qr = QRCode(border=1, box_size=box_size)
127
178
  qr.add_data(loginurl)
@@ -136,11 +187,14 @@ class mijiaLogin(object):
136
187
  'Or just use the qr.png file in the current directory.')
137
188
 
138
189
  def QRlogin(self) -> dict:
139
- """login with QR code
140
- mijiaLogin.QRlogin() -> dict
141
- -------
142
- @return
143
- dict, data for authorization, including userId, ssecurity, deviceId, serviceToken
190
+ """
191
+ 使用二维码登录。
192
+
193
+ Returns:
194
+ dict: 授权数据,包含userIdssecuritydeviceIdserviceToken
195
+
196
+ Raises:
197
+ LoginError: 登录失败时抛出。
144
198
  """
145
199
  data = self._get_index()
146
200
  location = data['location']
@@ -191,10 +245,12 @@ class mijiaLogin(object):
191
245
  self.auth_data = auth_data
192
246
  self.auth_data.update(self._get_account(auth_data['userId']))
193
247
 
194
- if self.save_auth:
195
- self._save_auth()
248
+ self._save_auth()
196
249
  return auth_data
197
250
 
198
251
  def __del__(self):
252
+ """
253
+ 析构函数,清理生成的二维码文件。
254
+ """
199
255
  if os.path.exists('qr.png'):
200
256
  os.remove('qr.png')
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mijiaAPI"
3
- version = "1.3.10"
3
+ version = "1.3.12"
4
4
  description = "A Python API for Xiaomi Mijia"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9,<4.0"
@@ -12,7 +12,7 @@ dependencies = [
12
12
 
13
13
  [tool.poetry]
14
14
  name = "mijiaAPI"
15
- version = "1.3.10"
15
+ version = "1.3.12"
16
16
  description = "A Python API for Xiaomi Mijia"
17
17
  authors = ["Do1e <dpj.email@qq.com>"]
18
18
  license = "GPLv3"
@@ -1,171 +0,0 @@
1
- from typing import Union
2
- import requests
3
- import requests.cookies
4
-
5
- from .code import ERROR_CODE
6
- from .utils import defaultUA, post_data, PostDataError
7
-
8
-
9
- class mijiaAPI(object):
10
- def __init__(self, auth_data: dict):
11
- if any(k not in auth_data for k in ['userId', 'deviceId', 'ssecurity', 'serviceToken']):
12
- raise Exception('Invalid authorize data')
13
- self.userId = auth_data['userId']
14
- self.ssecurity = auth_data['ssecurity']
15
- self.session = requests.Session()
16
- self.session.headers.update({
17
- 'User-Agent': defaultUA,
18
- 'x-xiaomi-protocal-flag-cli': 'PROTOCAL-HTTP2',
19
- 'Cookie': f'PassportDeviceId={auth_data["deviceId"]};'
20
- f'userId={auth_data["userId"]};'
21
- f'serviceToken={auth_data["serviceToken"]};',
22
- })
23
-
24
- @staticmethod
25
- def _post_process(data: dict) -> Union[list, bool]:
26
- if data['code'] != 0:
27
- raise Exception(f'Failed to get data, {data["message"]}')
28
- return data['result']
29
-
30
- @property
31
- def available(self) -> bool:
32
- """check if the API is available"""
33
- uri = '/home/device_list'
34
- data = {"getVirtualModel": False, "getHuamiDevices": 0}
35
- try:
36
- post_data(self.session, self.ssecurity, uri, data)
37
- return True
38
- except PostDataError:
39
- return False
40
-
41
- def get_devices_list(self) -> dict:
42
- """get devices list
43
- mijiaAPI.get_devices_list() -> dict
44
- -------
45
- @return
46
- dict, devices list
47
- """
48
- uri = '/home/device_list'
49
- data = {"getVirtualModel": False, "getHuamiDevices": 0}
50
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
51
-
52
- def get_homes_list(self) -> list:
53
- """get homes list
54
- mijiaAPI.get_homes_list() -> list
55
- -------
56
- @return
57
- list, homes list, including rooms
58
- """
59
- uri = '/v2/homeroom/gethome'
60
- data = {"fg": False, "fetch_share": True, "fetch_share_dev": True, "limit": 300, "app_ver": 7}
61
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
62
-
63
- def get_scenes_list(self, home_id: str) -> list:
64
- """get scenes list
65
- set it in Mi Home APP -> Add -> Manual controls
66
- mijiaAPI.get_scenes_list(home_id: str) -> list
67
- -------
68
- @param
69
- home_id: str, room id, get from get_homes_list
70
- -------
71
- @return
72
- list, scenes list
73
- """
74
- uri = '/appgateway/miot/appsceneservice/AppSceneService/GetSceneList'
75
- data = {"home_id": home_id}
76
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
77
-
78
- def run_scene(self, scene_id: str) -> bool:
79
- """run scene
80
- mijiaAPI.run_scene(scene_id: str) -> bool
81
- -------
82
- @param
83
- scene_id: str, scene id, get from get_scenes_list
84
- -------
85
- @return
86
- dict, result
87
- """
88
- uri = '/appgateway/miot/appsceneservice/AppSceneService/RunScene'
89
- data = {"scene_id": scene_id, "trigger_key": "user.click"}
90
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
91
-
92
- def get_consumable_items(self, home_id: str) -> list:
93
- """get consumable items
94
- mijiaAPI.get_consumable_items(did: str) -> list
95
- -------
96
- @param
97
- home_id: str, room id, get from get_homes_list
98
- -------
99
- @return
100
- list, consumable items
101
- """
102
- uri = '/v2/home/standard_consumable_items'
103
- data = {"home_id": int(home_id), "owner_id": self.userId}
104
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
105
-
106
- def get_devices_prop(self, data: list) -> list:
107
- """get devices properties
108
- mijiaAPI.get_devices_prop(data: list) -> list
109
- -------
110
- @param
111
- data: list of dict
112
- dict keys:
113
- - did: str, device id, get from get_devices_list
114
- - siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
115
- - piid: str, property id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
116
- model yeelink.light.lamp4 as an example:
117
- [
118
- {"did": "1234567890", "siid": 2, "piid": 2}, # get the brightness
119
- {"did": "1234567890", "siid": 2, "piid": 3}, # get the color temperature
120
- ]
121
- -------
122
- @return
123
- list, device properties
124
- """
125
- uri = '/miotspec/prop/get'
126
- data = {"params": data}
127
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
128
-
129
- def set_devices_prop(self, data: list) -> list:
130
- """set devices properties
131
- mijiaAPI.set_devices_prop(data: list) -> list
132
- -------
133
- @param
134
- data: list of dict
135
- dict keys:
136
- - did: str, device id, get from get_devices_list
137
- - siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
138
- - piid: str, property id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
139
- - value: str, value to set
140
- model yeelink.light.lamp4 as an example:
141
- [
142
- {"did": "1234567890", "siid": 2, "piid": 2, "value": 50} # set the brightness to 50%
143
- {"did": "1234567890", "siid": 2, "piid": 3, "value": 2700} # set the color temperature to 2700K
144
- ]
145
- -------
146
- @return
147
- dict, result
148
- """
149
- uri = '/miotspec/prop/set'
150
- data = {"params": data}
151
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
152
-
153
- def run_action(self, data: dict) -> dict:
154
- """run action
155
- mijiaAPI.run_action(data: dict) -> dict
156
- @param
157
- data: dict
158
- dict keys:
159
- - did: str, device id, get from get_devices_list
160
- - siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
161
- - aiid: str, action id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
162
- - value: list, value to list
163
- model xiaomi.feeder.pi2001 as an example:
164
- {"did": "1234567890", "siid": 2, "aiid": 1, "value": [2]}, # Remote feeding (2 servings) of food
165
- -------
166
- @return
167
- dict, result
168
- """
169
- uri = '/miotspec/action'
170
- data = {"params": data}
171
- return self._post_process(post_data(self.session, self.ssecurity, uri, data))
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes