mijiaAPI 1.3.3__tar.gz → 1.3.4__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.3.3 → mijiaapi-1.3.4}/PKG-INFO +3 -3
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/README.md +51 -51
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/mijiaAPI/apis.py +169 -169
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/mijiaAPI/devices.py +23 -15
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/mijiaAPI/urls.py +6 -6
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/mijiaAPI/utils.py +43 -43
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/pyproject.toml +1 -1
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/LICENSE +0 -0
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/mijiaAPI/__init__.py +0 -0
- {mijiaapi-1.3.3 → mijiaapi-1.3.4}/mijiaAPI/login.py +0 -0
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: mijiaAPI
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.4
|
|
4
4
|
Summary: A Python API for Xiaomi Mijia
|
|
5
|
-
Home-page: https://github.com/Do1e/mijia-api
|
|
6
5
|
License: GPLv3
|
|
7
6
|
Author: Do1e
|
|
8
7
|
Author-email: dpj.email@qq.com
|
|
@@ -19,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
19
18
|
Requires-Dist: pillow (>=11.0.0,<12.0.0)
|
|
20
19
|
Requires-Dist: qrcode (>=8.0,<9.0)
|
|
21
20
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
21
|
+
Project-URL: Homepage, https://github.com/Do1e/mijia-api
|
|
22
22
|
Project-URL: Repository, https://github.com/Do1e/mijia-api
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
# mijiaAPI
|
|
2
|
-
小米米家设备的api,可以使用代码直接控制米家设备的功能,[Github link](https://github.com/Do1e/mijia-api),[PyPI link](https://pypi.org/project/mijiaAPI/)。
|
|
3
|
-
|
|
4
|
-
## 安装
|
|
5
|
-
```bash
|
|
6
|
-
poetry install
|
|
7
|
-
```
|
|
8
|
-
或者
|
|
9
|
-
```bash
|
|
10
|
-
pip install mijiaAPI
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## 使用
|
|
14
|
-
使用实例可以参考`demos`文件夹下的示例代码,以下是简单的使用说明
|
|
15
|
-
|
|
16
|
-
有三个类分别用于登录和API调用
|
|
17
|
-
|
|
18
|
-
* `mijiaLogin`:登录小米账号,获取控制设备必须的`userId`, `ssecurity`, `deviceId`, `serviceToken`,方法列表
|
|
19
|
-
* `login(username: str, password: str) -> dict`:账号密码登录,返回上述信息
|
|
20
|
-
* `QRlogin() -> dict`:扫描二维码登录,返回上述信息(会在支持tty的终端打印二维码,若打印识别可查看当前文件夹下的`qr.png`)
|
|
21
|
-
|
|
22
|
-
* `mijiaAPI`:API的实现,使用`mijiaLogin`登录后返回的信息进行初始化
|
|
23
|
-
* `__init__(auth_data: dict)`:初始化
|
|
24
|
-
* `available -> bool`:传入的`auth_data`是否有效
|
|
25
|
-
* `get_devices_list() -> list`:获取设备列表
|
|
26
|
-
* `get_homes_list() -> list`:获取家庭列表,家庭字典中包含房间列表
|
|
27
|
-
* `get_scenes_list(home_id: str) -> list`:获取手动场景列表,在 *米家->添加->手动控制* 中设置
|
|
28
|
-
* `run_scene(scene_id: str) -> bool`:运行手动场景
|
|
29
|
-
* `get_consumable_items(home_id: str) -> list`:获取设备的耗材信息
|
|
30
|
-
* `get_devices_prop(data: list) -> list`:获取设备的属性
|
|
31
|
-
* `set_devices_prop(data: list) -> list`:设置设备的属性
|
|
32
|
-
* `data`为一个字典的列表,字典需要包含`did`, `siid`, `piid`,后面两个键可从 *https://home.miot-spec.com/spec/{model}* 中获取,其中`model`为设备的model,在设备列表中获取,如[米家台灯 1S](https://home.miot-spec.com/spec/yeelink.light.lamp4)。
|
|
33
|
-
* 网站上的方法并非全部可用,需要自行测试
|
|
34
|
-
* `run_action(data: dict) -> dict`:执行设备的action
|
|
35
|
-
* `data`为一个字典,需要包含`did`, `siid`, `aiid`,获取方法同上
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* `mijiaDevices`:使用`mijiaAPI`和设备属性字典初始化,以便更方便地调用设备属性
|
|
39
|
-
* `__init__(api: mijiaAPI, dev_info: dict. did: str = None, sleep_time: float = 0.5)`:初始化,`dev_info`为设备属性,参考[demos/dev_info_example](demos/dev_info_example),`sleep_time`为每次调用设备属性的间隔时间(注:设置属性后立刻获取属性会不符合预期,需要延迟一段时间)
|
|
40
|
-
* `set(name: str, did: str, value: Union[bool, int]) -> Union[bool, int]`:设置设备属性
|
|
41
|
-
* `get(name: str, did: str) -> Union[bool, int]`:获取设备属性
|
|
42
|
-
* v1.2.0 新增直接通过名称设置/获取属性,需要在初始化时传入`did`,详见[demos/test_devices_v2_light.py](demos/test_devices_v2_light.py)。名称中包含`-`的属性需要替换为`_`。
|
|
43
|
-
* **欢迎大家把自己编写的设备属性字典分享到Issues中,方便大家使用**
|
|
44
|
-
* 也可以调用`get_device_info(device_model: str) -> dict`函数从[米家设备列表](https://home.miot-spec.com/)在线获取设备属性字典,详见[demos/test_get_device_info.py](demos/test_get_device_info.py)
|
|
45
|
-
|
|
46
|
-
## 致谢
|
|
47
|
-
* [janzlan/mijia-api](https://gitee.com/janzlan/mijia-api/tree/master)
|
|
48
|
-
|
|
49
|
-
## 声明
|
|
50
|
-
* 本项目仅供学习交流使用,不得用于商业用途,如有侵权请联系删除。
|
|
51
|
-
* 本项目作者不对使用本项目产生的任何后果负责,请用户自行承担使用本项目的风险。
|
|
1
|
+
# mijiaAPI
|
|
2
|
+
小米米家设备的api,可以使用代码直接控制米家设备的功能,[Github link](https://github.com/Do1e/mijia-api),[PyPI link](https://pypi.org/project/mijiaAPI/)。
|
|
3
|
+
|
|
4
|
+
## 安装
|
|
5
|
+
```bash
|
|
6
|
+
poetry install
|
|
7
|
+
```
|
|
8
|
+
或者
|
|
9
|
+
```bash
|
|
10
|
+
pip install mijiaAPI
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 使用
|
|
14
|
+
使用实例可以参考`demos`文件夹下的示例代码,以下是简单的使用说明
|
|
15
|
+
|
|
16
|
+
有三个类分别用于登录和API调用
|
|
17
|
+
|
|
18
|
+
* `mijiaLogin`:登录小米账号,获取控制设备必须的`userId`, `ssecurity`, `deviceId`, `serviceToken`,方法列表
|
|
19
|
+
* `login(username: str, password: str) -> dict`:账号密码登录,返回上述信息
|
|
20
|
+
* `QRlogin() -> dict`:扫描二维码登录,返回上述信息(会在支持tty的终端打印二维码,若打印识别可查看当前文件夹下的`qr.png`)
|
|
21
|
+
|
|
22
|
+
* `mijiaAPI`:API的实现,使用`mijiaLogin`登录后返回的信息进行初始化
|
|
23
|
+
* `__init__(auth_data: dict)`:初始化
|
|
24
|
+
* `available -> bool`:传入的`auth_data`是否有效
|
|
25
|
+
* `get_devices_list() -> list`:获取设备列表
|
|
26
|
+
* `get_homes_list() -> list`:获取家庭列表,家庭字典中包含房间列表
|
|
27
|
+
* `get_scenes_list(home_id: str) -> list`:获取手动场景列表,在 *米家->添加->手动控制* 中设置
|
|
28
|
+
* `run_scene(scene_id: str) -> bool`:运行手动场景
|
|
29
|
+
* `get_consumable_items(home_id: str) -> list`:获取设备的耗材信息
|
|
30
|
+
* `get_devices_prop(data: list) -> list`:获取设备的属性
|
|
31
|
+
* `set_devices_prop(data: list) -> list`:设置设备的属性
|
|
32
|
+
* `data`为一个字典的列表,字典需要包含`did`, `siid`, `piid`,后面两个键可从 *https://home.miot-spec.com/spec/{model}* 中获取,其中`model`为设备的model,在设备列表中获取,如[米家台灯 1S](https://home.miot-spec.com/spec/yeelink.light.lamp4)。
|
|
33
|
+
* 网站上的方法并非全部可用,需要自行测试
|
|
34
|
+
* `run_action(data: dict) -> dict`:执行设备的action
|
|
35
|
+
* `data`为一个字典,需要包含`did`, `siid`, `aiid`,获取方法同上
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
* `mijiaDevices`:使用`mijiaAPI`和设备属性字典初始化,以便更方便地调用设备属性
|
|
39
|
+
* `__init__(api: mijiaAPI, dev_info: dict. did: str = None, sleep_time: float = 0.5)`:初始化,`dev_info`为设备属性,参考[demos/dev_info_example](demos/dev_info_example),`sleep_time`为每次调用设备属性的间隔时间(注:设置属性后立刻获取属性会不符合预期,需要延迟一段时间)
|
|
40
|
+
* `set(name: str, did: str, value: Union[bool, int]) -> Union[bool, int]`:设置设备属性
|
|
41
|
+
* `get(name: str, did: str) -> Union[bool, int]`:获取设备属性
|
|
42
|
+
* v1.2.0 新增直接通过名称设置/获取属性,需要在初始化时传入`did`,详见[demos/test_devices_v2_light.py](demos/test_devices_v2_light.py)。名称中包含`-`的属性需要替换为`_`。
|
|
43
|
+
* **欢迎大家把自己编写的设备属性字典分享到Issues中,方便大家使用**
|
|
44
|
+
* 也可以调用`get_device_info(device_model: str) -> dict`函数从[米家设备列表](https://home.miot-spec.com/)在线获取设备属性字典,详见[demos/test_get_device_info.py](demos/test_get_device_info.py)
|
|
45
|
+
|
|
46
|
+
## 致谢
|
|
47
|
+
* [janzlan/mijia-api](https://gitee.com/janzlan/mijia-api/tree/master)
|
|
48
|
+
|
|
49
|
+
## 声明
|
|
50
|
+
* 本项目仅供学习交流使用,不得用于商业用途,如有侵权请联系删除。
|
|
51
|
+
* 本项目作者不对使用本项目产生的任何后果负责,请用户自行承担使用本项目的风险。
|
|
@@ -1,169 +1,169 @@
|
|
|
1
|
-
from typing import Union
|
|
2
|
-
import requests
|
|
3
|
-
import requests.cookies
|
|
4
|
-
|
|
5
|
-
from .utils import defaultUA, post_data, PostDataError
|
|
6
|
-
|
|
7
|
-
class mijiaAPI(object):
|
|
8
|
-
def __init__(self, auth_data: dict):
|
|
9
|
-
if any(k not in auth_data for k in ['userId', 'deviceId', 'ssecurity', 'serviceToken']):
|
|
10
|
-
raise Exception('Invalid authorize data')
|
|
11
|
-
self.userId = auth_data['userId']
|
|
12
|
-
self.ssecurity = auth_data['ssecurity']
|
|
13
|
-
self.session = requests.Session()
|
|
14
|
-
self.session.headers.update({
|
|
15
|
-
'User-Agent': defaultUA,
|
|
16
|
-
'x-xiaomi-protocal-flag-cli': 'PROTOCAL-HTTP2',
|
|
17
|
-
'Cookie': f'PassportDeviceId={auth_data["deviceId"]};'
|
|
18
|
-
f'userId={auth_data["userId"]};'
|
|
19
|
-
f'serviceToken={auth_data["serviceToken"]};',
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
@staticmethod
|
|
23
|
-
def _post_process(data: dict) -> Union[list, bool]:
|
|
24
|
-
if data['code'] != 0:
|
|
25
|
-
raise Exception(f'Failed to get data, {data["message"]}')
|
|
26
|
-
return data['result']
|
|
27
|
-
|
|
28
|
-
@property
|
|
29
|
-
def available(self) -> bool:
|
|
30
|
-
"""check if the API is available"""
|
|
31
|
-
uri = '/home/device_list'
|
|
32
|
-
data = {"getVirtualModel": False, "getHuamiDevices": 0}
|
|
33
|
-
try:
|
|
34
|
-
post_data(self.session, self.ssecurity, uri, data)
|
|
35
|
-
return True
|
|
36
|
-
except PostDataError:
|
|
37
|
-
return False
|
|
38
|
-
|
|
39
|
-
def get_devices_list(self) -> list:
|
|
40
|
-
"""get devices list
|
|
41
|
-
mijiaAPI.get_devices_list() -> list
|
|
42
|
-
-------
|
|
43
|
-
@return
|
|
44
|
-
dict, devices list
|
|
45
|
-
"""
|
|
46
|
-
uri = '/home/device_list'
|
|
47
|
-
data = {"getVirtualModel": False, "getHuamiDevices": 0}
|
|
48
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
49
|
-
|
|
50
|
-
def get_homes_list(self) -> list:
|
|
51
|
-
"""get homes list
|
|
52
|
-
mijiaAPI.get_homes_list() -> list
|
|
53
|
-
-------
|
|
54
|
-
@return
|
|
55
|
-
list, homes list, including rooms
|
|
56
|
-
"""
|
|
57
|
-
uri = '/v2/homeroom/gethome'
|
|
58
|
-
data = {"fg": False, "fetch_share": True, "fetch_share_dev": True, "limit": 300, "app_ver": 7}
|
|
59
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
60
|
-
|
|
61
|
-
def get_scenes_list(self, home_id: str) -> list:
|
|
62
|
-
"""get scenes list
|
|
63
|
-
set it in Mi Home APP -> Add -> Manual controls
|
|
64
|
-
mijiaAPI.get_scenes_list(home_id: str) -> list
|
|
65
|
-
-------
|
|
66
|
-
@param
|
|
67
|
-
home_id: str, room id, get from get_homes_list
|
|
68
|
-
-------
|
|
69
|
-
@return
|
|
70
|
-
list, scenes list
|
|
71
|
-
"""
|
|
72
|
-
uri = '/appgateway/miot/appsceneservice/AppSceneService/GetSceneList'
|
|
73
|
-
data = {"home_id": home_id}
|
|
74
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
75
|
-
|
|
76
|
-
def run_scene(self, scene_id: str) -> bool:
|
|
77
|
-
"""run scene
|
|
78
|
-
mijiaAPI.run_scene(scene_id: str) -> bool
|
|
79
|
-
-------
|
|
80
|
-
@param
|
|
81
|
-
scene_id: str, scene id, get from get_scenes_list
|
|
82
|
-
-------
|
|
83
|
-
@return
|
|
84
|
-
dict, result
|
|
85
|
-
"""
|
|
86
|
-
uri = '/appgateway/miot/appsceneservice/AppSceneService/RunScene'
|
|
87
|
-
data = {"scene_id": scene_id, "trigger_key": "user.click"}
|
|
88
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
89
|
-
|
|
90
|
-
def get_consumable_items(self, home_id: str) -> list:
|
|
91
|
-
"""get consumable items
|
|
92
|
-
mijiaAPI.get_consumable_items(did: str) -> list
|
|
93
|
-
-------
|
|
94
|
-
@param
|
|
95
|
-
home_id: str, room id, get from get_homes_list
|
|
96
|
-
-------
|
|
97
|
-
@return
|
|
98
|
-
list, consumable items
|
|
99
|
-
"""
|
|
100
|
-
uri = '/v2/home/standard_consumable_items'
|
|
101
|
-
data = {"home_id": int(home_id), "owner_id": self.userId}
|
|
102
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
103
|
-
|
|
104
|
-
def get_devices_prop(self, data: list) -> list:
|
|
105
|
-
"""get devices properties
|
|
106
|
-
mijiaAPI.get_devices_prop(data: list) -> list
|
|
107
|
-
-------
|
|
108
|
-
@param
|
|
109
|
-
data: list of dict
|
|
110
|
-
dict keys:
|
|
111
|
-
- did: str, device id, get from get_devices_list
|
|
112
|
-
- siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
113
|
-
- piid: str, property id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
114
|
-
model yeelink.light.lamp4 as an example:
|
|
115
|
-
[
|
|
116
|
-
{"did": "1234567890", "siid": 2, "piid": 2}, # get the brightness
|
|
117
|
-
{"did": "1234567890", "siid": 2, "piid": 3}, # get the color temperature
|
|
118
|
-
]
|
|
119
|
-
-------
|
|
120
|
-
@return
|
|
121
|
-
list, device properties
|
|
122
|
-
"""
|
|
123
|
-
uri = '/miotspec/prop/get'
|
|
124
|
-
data = {"params": data}
|
|
125
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
126
|
-
|
|
127
|
-
def set_devices_prop(self, data: list) -> list:
|
|
128
|
-
"""set devices properties
|
|
129
|
-
mijiaAPI.set_devices_prop(data: list) -> list
|
|
130
|
-
-------
|
|
131
|
-
@param
|
|
132
|
-
data: list of dict
|
|
133
|
-
dict keys:
|
|
134
|
-
- did: str, device id, get from get_devices_list
|
|
135
|
-
- siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
136
|
-
- piid: str, property id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
137
|
-
- value: str, value to set
|
|
138
|
-
model yeelink.light.lamp4 as an example:
|
|
139
|
-
[
|
|
140
|
-
{"did": "1234567890", "siid": 2, "piid": 2, "value": 50} # set the brightness to 50%
|
|
141
|
-
{"did": "1234567890", "siid": 2, "piid": 3, "value": 2700} # set the color temperature to 2700K
|
|
142
|
-
]
|
|
143
|
-
-------
|
|
144
|
-
@return
|
|
145
|
-
dict, result
|
|
146
|
-
"""
|
|
147
|
-
uri = '/miotspec/prop/set'
|
|
148
|
-
data = {"params": data}
|
|
149
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
150
|
-
|
|
151
|
-
def run_action(self, data: dict) -> dict:
|
|
152
|
-
"""run action
|
|
153
|
-
mijiaAPI.run_action(data: dict) -> dict
|
|
154
|
-
@param
|
|
155
|
-
data: dict
|
|
156
|
-
dict keys:
|
|
157
|
-
- did: str, device id, get from get_devices_list
|
|
158
|
-
- siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
159
|
-
- aiid: str, action id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
160
|
-
- value: list, value to list
|
|
161
|
-
model xiaomi.feeder.pi2001 as an example:
|
|
162
|
-
{"did": "1234567890", "siid": 2, "aiid": 1, "value": [2]}, # Remote feeding (2 servings) of food
|
|
163
|
-
-------
|
|
164
|
-
@return
|
|
165
|
-
dict, result
|
|
166
|
-
"""
|
|
167
|
-
uri = '/miotspec/action'
|
|
168
|
-
data = {"params": data}
|
|
169
|
-
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
1
|
+
from typing import Union
|
|
2
|
+
import requests
|
|
3
|
+
import requests.cookies
|
|
4
|
+
|
|
5
|
+
from .utils import defaultUA, post_data, PostDataError
|
|
6
|
+
|
|
7
|
+
class mijiaAPI(object):
|
|
8
|
+
def __init__(self, auth_data: dict):
|
|
9
|
+
if any(k not in auth_data for k in ['userId', 'deviceId', 'ssecurity', 'serviceToken']):
|
|
10
|
+
raise Exception('Invalid authorize data')
|
|
11
|
+
self.userId = auth_data['userId']
|
|
12
|
+
self.ssecurity = auth_data['ssecurity']
|
|
13
|
+
self.session = requests.Session()
|
|
14
|
+
self.session.headers.update({
|
|
15
|
+
'User-Agent': defaultUA,
|
|
16
|
+
'x-xiaomi-protocal-flag-cli': 'PROTOCAL-HTTP2',
|
|
17
|
+
'Cookie': f'PassportDeviceId={auth_data["deviceId"]};'
|
|
18
|
+
f'userId={auth_data["userId"]};'
|
|
19
|
+
f'serviceToken={auth_data["serviceToken"]};',
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
@staticmethod
|
|
23
|
+
def _post_process(data: dict) -> Union[list, bool]:
|
|
24
|
+
if data['code'] != 0:
|
|
25
|
+
raise Exception(f'Failed to get data, {data["message"]}')
|
|
26
|
+
return data['result']
|
|
27
|
+
|
|
28
|
+
@property
|
|
29
|
+
def available(self) -> bool:
|
|
30
|
+
"""check if the API is available"""
|
|
31
|
+
uri = '/home/device_list'
|
|
32
|
+
data = {"getVirtualModel": False, "getHuamiDevices": 0}
|
|
33
|
+
try:
|
|
34
|
+
post_data(self.session, self.ssecurity, uri, data)
|
|
35
|
+
return True
|
|
36
|
+
except PostDataError:
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
def get_devices_list(self) -> list:
|
|
40
|
+
"""get devices list
|
|
41
|
+
mijiaAPI.get_devices_list() -> list
|
|
42
|
+
-------
|
|
43
|
+
@return
|
|
44
|
+
dict, devices list
|
|
45
|
+
"""
|
|
46
|
+
uri = '/home/device_list'
|
|
47
|
+
data = {"getVirtualModel": False, "getHuamiDevices": 0}
|
|
48
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
49
|
+
|
|
50
|
+
def get_homes_list(self) -> list:
|
|
51
|
+
"""get homes list
|
|
52
|
+
mijiaAPI.get_homes_list() -> list
|
|
53
|
+
-------
|
|
54
|
+
@return
|
|
55
|
+
list, homes list, including rooms
|
|
56
|
+
"""
|
|
57
|
+
uri = '/v2/homeroom/gethome'
|
|
58
|
+
data = {"fg": False, "fetch_share": True, "fetch_share_dev": True, "limit": 300, "app_ver": 7}
|
|
59
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
60
|
+
|
|
61
|
+
def get_scenes_list(self, home_id: str) -> list:
|
|
62
|
+
"""get scenes list
|
|
63
|
+
set it in Mi Home APP -> Add -> Manual controls
|
|
64
|
+
mijiaAPI.get_scenes_list(home_id: str) -> list
|
|
65
|
+
-------
|
|
66
|
+
@param
|
|
67
|
+
home_id: str, room id, get from get_homes_list
|
|
68
|
+
-------
|
|
69
|
+
@return
|
|
70
|
+
list, scenes list
|
|
71
|
+
"""
|
|
72
|
+
uri = '/appgateway/miot/appsceneservice/AppSceneService/GetSceneList'
|
|
73
|
+
data = {"home_id": home_id}
|
|
74
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
75
|
+
|
|
76
|
+
def run_scene(self, scene_id: str) -> bool:
|
|
77
|
+
"""run scene
|
|
78
|
+
mijiaAPI.run_scene(scene_id: str) -> bool
|
|
79
|
+
-------
|
|
80
|
+
@param
|
|
81
|
+
scene_id: str, scene id, get from get_scenes_list
|
|
82
|
+
-------
|
|
83
|
+
@return
|
|
84
|
+
dict, result
|
|
85
|
+
"""
|
|
86
|
+
uri = '/appgateway/miot/appsceneservice/AppSceneService/RunScene'
|
|
87
|
+
data = {"scene_id": scene_id, "trigger_key": "user.click"}
|
|
88
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
89
|
+
|
|
90
|
+
def get_consumable_items(self, home_id: str) -> list:
|
|
91
|
+
"""get consumable items
|
|
92
|
+
mijiaAPI.get_consumable_items(did: str) -> list
|
|
93
|
+
-------
|
|
94
|
+
@param
|
|
95
|
+
home_id: str, room id, get from get_homes_list
|
|
96
|
+
-------
|
|
97
|
+
@return
|
|
98
|
+
list, consumable items
|
|
99
|
+
"""
|
|
100
|
+
uri = '/v2/home/standard_consumable_items'
|
|
101
|
+
data = {"home_id": int(home_id), "owner_id": self.userId}
|
|
102
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
103
|
+
|
|
104
|
+
def get_devices_prop(self, data: list) -> list:
|
|
105
|
+
"""get devices properties
|
|
106
|
+
mijiaAPI.get_devices_prop(data: list) -> list
|
|
107
|
+
-------
|
|
108
|
+
@param
|
|
109
|
+
data: list of dict
|
|
110
|
+
dict keys:
|
|
111
|
+
- did: str, device id, get from get_devices_list
|
|
112
|
+
- siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
113
|
+
- piid: str, property id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
114
|
+
model yeelink.light.lamp4 as an example:
|
|
115
|
+
[
|
|
116
|
+
{"did": "1234567890", "siid": 2, "piid": 2}, # get the brightness
|
|
117
|
+
{"did": "1234567890", "siid": 2, "piid": 3}, # get the color temperature
|
|
118
|
+
]
|
|
119
|
+
-------
|
|
120
|
+
@return
|
|
121
|
+
list, device properties
|
|
122
|
+
"""
|
|
123
|
+
uri = '/miotspec/prop/get'
|
|
124
|
+
data = {"params": data}
|
|
125
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
126
|
+
|
|
127
|
+
def set_devices_prop(self, data: list) -> list:
|
|
128
|
+
"""set devices properties
|
|
129
|
+
mijiaAPI.set_devices_prop(data: list) -> list
|
|
130
|
+
-------
|
|
131
|
+
@param
|
|
132
|
+
data: list of dict
|
|
133
|
+
dict keys:
|
|
134
|
+
- did: str, device id, get from get_devices_list
|
|
135
|
+
- siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
136
|
+
- piid: str, property id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
137
|
+
- value: str, value to set
|
|
138
|
+
model yeelink.light.lamp4 as an example:
|
|
139
|
+
[
|
|
140
|
+
{"did": "1234567890", "siid": 2, "piid": 2, "value": 50} # set the brightness to 50%
|
|
141
|
+
{"did": "1234567890", "siid": 2, "piid": 3, "value": 2700} # set the color temperature to 2700K
|
|
142
|
+
]
|
|
143
|
+
-------
|
|
144
|
+
@return
|
|
145
|
+
dict, result
|
|
146
|
+
"""
|
|
147
|
+
uri = '/miotspec/prop/set'
|
|
148
|
+
data = {"params": data}
|
|
149
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
150
|
+
|
|
151
|
+
def run_action(self, data: dict) -> dict:
|
|
152
|
+
"""run action
|
|
153
|
+
mijiaAPI.run_action(data: dict) -> dict
|
|
154
|
+
@param
|
|
155
|
+
data: dict
|
|
156
|
+
dict keys:
|
|
157
|
+
- did: str, device id, get from get_devices_list
|
|
158
|
+
- siid: str, service id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
159
|
+
- aiid: str, action id, get from https://home.miot-spec.com/spec/{model}, model from get_devices_list
|
|
160
|
+
- value: list, value to list
|
|
161
|
+
model xiaomi.feeder.pi2001 as an example:
|
|
162
|
+
{"did": "1234567890", "siid": 2, "aiid": 1, "value": [2]}, # Remote feeding (2 servings) of food
|
|
163
|
+
-------
|
|
164
|
+
@return
|
|
165
|
+
dict, result
|
|
166
|
+
"""
|
|
167
|
+
uri = '/miotspec/action'
|
|
168
|
+
data = {"params": data}
|
|
169
|
+
return self._post_process(post_data(self.session, self.ssecurity, uri, data))
|
|
@@ -69,21 +69,21 @@ class mijiaDevices(object):
|
|
|
69
69
|
if not isinstance(value, bool):
|
|
70
70
|
raise ValueError(f'Invalid value for bool: {value}, should be True or False')
|
|
71
71
|
elif prop.type in ['int', 'uint']:
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if prop.range:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
72
|
+
value = int(value)
|
|
73
|
+
if prop.range:
|
|
74
|
+
if value < prop.range[0] or value > prop.range[1]:
|
|
75
|
+
raise ValueError(f'Value out of range: {value}, should be in range {prop.range[:2]}')
|
|
76
|
+
if len(prop.range) >= 3 and prop.range[2] != 1:
|
|
77
|
+
if (value - prop.range[0]) % prop.range[2] != 0:
|
|
78
|
+
raise ValueError(f'Invalid value: {value}, should be in range {prop.range[:2]} with step {prop.range[2]}')
|
|
79
79
|
elif prop.type == 'float':
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if prop.range:
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
value = float(value)
|
|
81
|
+
if prop.range:
|
|
82
|
+
if value < prop.range[0] or value > prop.range[1]:
|
|
83
|
+
raise ValueError(f'Value out of range: {value}, should be in range {prop.range[:2]}')
|
|
84
|
+
if len(prop.range) >= 3 and isinstance(prop.range[2], int):
|
|
85
|
+
if int(value - prop.range[0]) % prop.range[2] != 0:
|
|
86
|
+
raise ValueError(f'Invalid value: {value}, should be in range {prop.range[:2]} with step {prop.range[2]}')
|
|
87
87
|
elif prop.type == 'string':
|
|
88
88
|
if not isinstance(value, str):
|
|
89
89
|
raise ValueError(f'Invalid value for string: {value}, should be a string')
|
|
@@ -166,6 +166,8 @@ def get_device_info(device_model: str) -> dict:
|
|
|
166
166
|
result['actions'] = []
|
|
167
167
|
services = content['props']['spec']['services']
|
|
168
168
|
|
|
169
|
+
properties_name = []
|
|
170
|
+
actions_name = []
|
|
169
171
|
for siid in services:
|
|
170
172
|
if 'properties' in services[siid]:
|
|
171
173
|
for piid in services[siid]['properties']:
|
|
@@ -193,11 +195,17 @@ def get_device_info(device_model: str) -> dict:
|
|
|
193
195
|
}
|
|
194
196
|
}
|
|
195
197
|
if item['range'] is not None:
|
|
196
|
-
item['range'] = item['range']
|
|
198
|
+
item['range'] = item['range']
|
|
199
|
+
if item['name'] in properties_name:
|
|
200
|
+
item["name"] = f'{services[siid]['name']}-{item["name"]}'
|
|
201
|
+
properties_name.append(item['name'])
|
|
197
202
|
result['properties'].append({k: None if v == 'none' else v for k, v in item.items()})
|
|
198
203
|
if 'actions' in services[siid]:
|
|
199
204
|
for aiid in services[siid]['actions']:
|
|
200
205
|
act = services[siid]['actions'][aiid]
|
|
206
|
+
if act['name'] in actions_name:
|
|
207
|
+
act['name'] = f'{services[siid]["name"]}-{act["name"]}'
|
|
208
|
+
actions_name.append(act['name'])
|
|
201
209
|
result['actions'].append({
|
|
202
210
|
'name': act['name'],
|
|
203
211
|
'description': act['description'],
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
sid = 'xiaomiio'
|
|
2
|
-
msgURL = 'https://account.xiaomi.com/pass/serviceLogin?sid=%s&_json=true' % sid
|
|
3
|
-
loginURL = 'https://account.xiaomi.com/pass/serviceLoginAuth2'
|
|
4
|
-
qrURL = 'https://account.xiaomi.com/longPolling/loginUrl'
|
|
5
|
-
apiURL = 'https://api.io.mi.com/app'
|
|
6
|
-
deviceURL = 'https://home.miot-spec.com/spec/'
|
|
1
|
+
sid = 'xiaomiio'
|
|
2
|
+
msgURL = 'https://account.xiaomi.com/pass/serviceLogin?sid=%s&_json=true' % sid
|
|
3
|
+
loginURL = 'https://account.xiaomi.com/pass/serviceLoginAuth2'
|
|
4
|
+
qrURL = 'https://account.xiaomi.com/longPolling/loginUrl'
|
|
5
|
+
apiURL = 'https://api.io.mi.com/app'
|
|
6
|
+
deviceURL = 'https://home.miot-spec.com/spec/'
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import base64
|
|
2
|
-
from hashlib import sha256
|
|
3
|
-
import hmac
|
|
4
|
-
import random
|
|
5
|
-
import string
|
|
6
|
-
|
|
7
|
-
import requests
|
|
8
|
-
|
|
9
|
-
from .urls import apiURL
|
|
10
|
-
|
|
11
|
-
defaultUA = 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36 Edg/126.0.0.0'
|
|
12
|
-
|
|
13
|
-
class PostDataError(Exception):
|
|
14
|
-
def __init__(self, code: int, message: str):
|
|
15
|
-
self.code = code
|
|
16
|
-
self.message = message
|
|
17
|
-
super().__init__(f'Error code: {code}, message: {message}')
|
|
18
|
-
|
|
19
|
-
def _generate_signed_nonce(secret: str, nonce: str) -> str:
|
|
20
|
-
sha = sha256()
|
|
21
|
-
sha.update(base64.b64decode(secret))
|
|
22
|
-
sha.update(base64.b64decode(nonce))
|
|
23
|
-
return base64.b64encode(sha.digest()).decode()
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _generate_signature(uri: str, signedNonce: str, nonce: str, data: str) -> str:
|
|
27
|
-
sign = '&'.join([uri, signedNonce, nonce, f'data={data}'])
|
|
28
|
-
mac = hmac.new(base64.b64decode(signedNonce), digestmod='sha256')
|
|
29
|
-
mac.update(sign.encode())
|
|
30
|
-
return base64.b64encode(mac.digest()).decode()
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def post_data(session: requests.Session, ssecurity: str, uri: str, data: dict) -> dict:
|
|
34
|
-
data = str(data).replace("'", '"').replace('True', 'true').replace('False', 'false')
|
|
35
|
-
nonce = ''.join(random.sample(string.digits + string.ascii_letters, 16))
|
|
36
|
-
signed_nonce = _generate_signed_nonce(ssecurity, nonce)
|
|
37
|
-
signature = _generate_signature(uri, signed_nonce, nonce, data)
|
|
38
|
-
post_data = {'_nonce': nonce, 'data': data, 'signature': signature}
|
|
39
|
-
ret = session.post(apiURL + uri, data=post_data)
|
|
40
|
-
if ret.status_code != 200:
|
|
41
|
-
raise PostDataError(ret.status_code, f'Failed to post data, {ret.text}')
|
|
42
|
-
ret_data = ret.json()
|
|
43
|
-
return ret_data
|
|
1
|
+
import base64
|
|
2
|
+
from hashlib import sha256
|
|
3
|
+
import hmac
|
|
4
|
+
import random
|
|
5
|
+
import string
|
|
6
|
+
|
|
7
|
+
import requests
|
|
8
|
+
|
|
9
|
+
from .urls import apiURL
|
|
10
|
+
|
|
11
|
+
defaultUA = 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Mobile Safari/537.36 Edg/126.0.0.0'
|
|
12
|
+
|
|
13
|
+
class PostDataError(Exception):
|
|
14
|
+
def __init__(self, code: int, message: str):
|
|
15
|
+
self.code = code
|
|
16
|
+
self.message = message
|
|
17
|
+
super().__init__(f'Error code: {code}, message: {message}')
|
|
18
|
+
|
|
19
|
+
def _generate_signed_nonce(secret: str, nonce: str) -> str:
|
|
20
|
+
sha = sha256()
|
|
21
|
+
sha.update(base64.b64decode(secret))
|
|
22
|
+
sha.update(base64.b64decode(nonce))
|
|
23
|
+
return base64.b64encode(sha.digest()).decode()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _generate_signature(uri: str, signedNonce: str, nonce: str, data: str) -> str:
|
|
27
|
+
sign = '&'.join([uri, signedNonce, nonce, f'data={data}'])
|
|
28
|
+
mac = hmac.new(base64.b64decode(signedNonce), digestmod='sha256')
|
|
29
|
+
mac.update(sign.encode())
|
|
30
|
+
return base64.b64encode(mac.digest()).decode()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def post_data(session: requests.Session, ssecurity: str, uri: str, data: dict) -> dict:
|
|
34
|
+
data = str(data).replace("'", '"').replace('True', 'true').replace('False', 'false')
|
|
35
|
+
nonce = ''.join(random.sample(string.digits + string.ascii_letters, 16))
|
|
36
|
+
signed_nonce = _generate_signed_nonce(ssecurity, nonce)
|
|
37
|
+
signature = _generate_signature(uri, signed_nonce, nonce, data)
|
|
38
|
+
post_data = {'_nonce': nonce, 'data': data, 'signature': signature}
|
|
39
|
+
ret = session.post(apiURL + uri, data=post_data)
|
|
40
|
+
if ret.status_code != 200:
|
|
41
|
+
raise PostDataError(ret.status_code, f'Failed to post data, {ret.text}')
|
|
42
|
+
ret_data = ret.json()
|
|
43
|
+
return ret_data
|
|
File without changes
|
|
File without changes
|
|
File without changes
|