python-aidot 0.2.5__tar.gz → 0.2.7__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.
- {python_aidot-0.2.5/python_aidot.egg-info → python_aidot-0.2.7}/PKG-INFO +1 -1
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/client.py +81 -37
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/const.py +14 -3
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/exceptions.py +5 -1
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/lan.py +2 -1
- {python_aidot-0.2.5 → python_aidot-0.2.7/python_aidot.egg-info}/PKG-INFO +1 -1
- {python_aidot-0.2.5 → python_aidot-0.2.7}/setup.py +1 -1
- {python_aidot-0.2.5 → python_aidot-0.2.7}/LICENSE +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/README.md +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/__init__.py +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/aes_utils.py +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/discover.py +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/login_const.py +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/login_control.py +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/aidot/login_data.py +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/python_aidot.egg-info/SOURCES.txt +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/python_aidot.egg-info/dependency_links.txt +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/python_aidot.egg-info/requires.txt +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/python_aidot.egg-info/top_level.txt +0 -0
- {python_aidot-0.2.5 → python_aidot-0.2.7}/setup.cfg +0 -0
|
@@ -8,7 +8,6 @@ import aiohttp
|
|
|
8
8
|
from cryptography.hazmat.backends import default_backend
|
|
9
9
|
from cryptography.hazmat.primitives import serialization
|
|
10
10
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
11
|
-
|
|
12
11
|
from .login_const import APP_ID, PUBLIC_KEY_PEM, BASE_URL
|
|
13
12
|
from .const import (
|
|
14
13
|
SUPPORTED_COUNTRYS,
|
|
@@ -20,8 +19,15 @@ from .const import (
|
|
|
20
19
|
CONF_REFRESH_TOKEN,
|
|
21
20
|
CONF_TERMINAL,
|
|
22
21
|
CONF_APP_ID,
|
|
22
|
+
CONF_REGION,
|
|
23
|
+
CONF_COUNTRY,
|
|
24
|
+
CONF_USERNAME,
|
|
25
|
+
CONF_PASSWORD,
|
|
26
|
+
CONF_CODE,
|
|
27
|
+
CONF_TOKEN,
|
|
28
|
+
ServerErrorCode
|
|
23
29
|
)
|
|
24
|
-
from .exceptions import AidotAuthFailed
|
|
30
|
+
from .exceptions import AidotAuthFailed,AidotUserOrPassIncorrect
|
|
25
31
|
|
|
26
32
|
_LOGGER = logging.getLogger(__name__)
|
|
27
33
|
|
|
@@ -52,11 +58,13 @@ class AidotClient:
|
|
|
52
58
|
|
|
53
59
|
def __init__(
|
|
54
60
|
self,
|
|
61
|
+
session: Optional[ClientSession],
|
|
55
62
|
country_name: str | None = None,
|
|
56
63
|
username: str | None = None,
|
|
57
64
|
password: str | None = None,
|
|
58
65
|
token: dict | None = None,
|
|
59
66
|
) -> None:
|
|
67
|
+
self.session = session
|
|
60
68
|
self.country_name = country_name
|
|
61
69
|
self.username = username
|
|
62
70
|
self.password = password
|
|
@@ -66,9 +74,20 @@ class AidotClient:
|
|
|
66
74
|
self._region = item["region"].lower()
|
|
67
75
|
self._base_url = f"https://prod-{self._region}-api.arnoo.com/v17"
|
|
68
76
|
break
|
|
77
|
+
if token is not None:
|
|
78
|
+
self.username = token[CONF_USERNAME]
|
|
79
|
+
self.password = token[CONF_PASSWORD]
|
|
80
|
+
self._region = token[CONF_REGION]
|
|
81
|
+
self.country_name = token[CONF_COUNTRY]
|
|
82
|
+
|
|
83
|
+
def set_token_fresh_cb(self, callback):
|
|
84
|
+
self._token_fresh_cb = callback
|
|
69
85
|
|
|
70
86
|
def get_identifier(self) -> str:
|
|
71
|
-
return f"{self.
|
|
87
|
+
return f"{self._region}-{self.username}"
|
|
88
|
+
|
|
89
|
+
def update_password(self, password: str):
|
|
90
|
+
self.password = password
|
|
72
91
|
|
|
73
92
|
async def async_post_login(self):
|
|
74
93
|
"""Login the user input allows us to connect."""
|
|
@@ -85,12 +104,18 @@ class AidotClient:
|
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
try:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
107
|
+
response = await self.session.post(url, headers=headers, json=data)
|
|
108
|
+
response_data = await response.json()
|
|
109
|
+
response.raise_for_status()
|
|
110
|
+
self.login_info = response_data
|
|
111
|
+
self.login_info[CONF_PASSWORD] = self.password
|
|
112
|
+
self.login_info[CONF_REGION] = self._region
|
|
113
|
+
self.login_info[CONF_COUNTRY] = self.country_name
|
|
114
|
+
return self.login_info
|
|
92
115
|
except aiohttp.ClientError as e:
|
|
93
|
-
_LOGGER.info("async_post_login ClientError {e}")
|
|
116
|
+
_LOGGER.info(f"async_post_login ClientError {e}")
|
|
117
|
+
if response_data[CONF_CODE] == ServerErrorCode.USER_PWD_INCORRECT:
|
|
118
|
+
raise AidotUserOrPassIncorrect
|
|
94
119
|
return None
|
|
95
120
|
|
|
96
121
|
async def async_refresh_token(self):
|
|
@@ -101,13 +126,20 @@ class AidotClient:
|
|
|
101
126
|
}
|
|
102
127
|
|
|
103
128
|
try:
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
response = await self.session.post(url, headers=headers, json=data)
|
|
130
|
+
response_data = await response.json()
|
|
131
|
+
response.raise_for_status()
|
|
132
|
+
self.login_info[CONF_ACCESS_TOKEN] = response_data[CONF_ACCESS_TOKEN]
|
|
133
|
+
if response_data[CONF_REFRESH_TOKEN] is not None:
|
|
134
|
+
self.login_info[CONF_REFRESH_TOKEN] = response_data[CONF_REFRESH_TOKEN]
|
|
135
|
+
_LOGGER.info(f"refresh token {response_data}")
|
|
136
|
+
if self._token_fresh_cb:
|
|
137
|
+
self._token_fresh_cb()
|
|
138
|
+
return response_data
|
|
109
139
|
except aiohttp.ClientError as e:
|
|
110
|
-
_LOGGER.info("
|
|
140
|
+
_LOGGER.info(f"async_refresh_token ClientError {e}")
|
|
141
|
+
if response_data[CONF_CODE] == ServerErrorCode.LOGIN_INVALID:
|
|
142
|
+
raise AidotAuthFailed
|
|
111
143
|
return None
|
|
112
144
|
|
|
113
145
|
async def async_session_get(self, params: str, headers: str | None = None):
|
|
@@ -118,16 +150,26 @@ class AidotClient:
|
|
|
118
150
|
if headers is None:
|
|
119
151
|
headers = {
|
|
120
152
|
CONF_TERMINAL: "app",
|
|
121
|
-
|
|
153
|
+
CONF_TOKEN: token,
|
|
122
154
|
CONF_APP_ID: APP_ID,
|
|
123
155
|
}
|
|
124
156
|
try:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
157
|
+
response = await self.session.get(url, headers=headers)
|
|
158
|
+
response_data = await response.json()
|
|
159
|
+
response.raise_for_status()
|
|
160
|
+
return response_data
|
|
129
161
|
except aiohttp.ClientError as e:
|
|
130
|
-
_LOGGER.info("
|
|
162
|
+
_LOGGER.info(f"async_get ClientError {e}")
|
|
163
|
+
code = response_data[CONF_CODE]
|
|
164
|
+
if code == ServerErrorCode.TOKEN_EXPIRED:
|
|
165
|
+
try:
|
|
166
|
+
await self.async_refresh_token()
|
|
167
|
+
return await self.async_session_get(params)
|
|
168
|
+
except AidotAuthFailed:
|
|
169
|
+
raise AidotAuthFailed
|
|
170
|
+
elif code == ServerErrorCode.LOGIN_INVALID or code == 21027 or code == 21041:
|
|
171
|
+
self.login_info[CONF_ACCESS_TOKEN] = None
|
|
172
|
+
raise AidotAuthFailed
|
|
131
173
|
return None
|
|
132
174
|
|
|
133
175
|
async def async_get_products(self, product_ids: str):
|
|
@@ -140,27 +182,29 @@ class AidotClient:
|
|
|
140
182
|
params = f"/devices?houseId={house_id}"
|
|
141
183
|
return await self.async_session_get(params)
|
|
142
184
|
|
|
143
|
-
async def async_get_houses(self
|
|
185
|
+
async def async_get_houses(self):
|
|
144
186
|
"""Get house list."""
|
|
145
187
|
params = "/houses"
|
|
146
188
|
return await self.async_session_get(params)
|
|
147
189
|
|
|
148
190
|
async def async_get_all_device(self):
|
|
149
|
-
# get houses
|
|
150
|
-
houses = await self.async_get_houses()
|
|
151
191
|
final_device_list: list[dict[str, Any]] = []
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
192
|
+
try:
|
|
193
|
+
houses = await self.async_get_houses()
|
|
194
|
+
for house in houses:
|
|
195
|
+
# get device_list
|
|
196
|
+
device_list = await self.async_get_devices(house[CONF_ID])
|
|
197
|
+
if device_list:
|
|
198
|
+
final_device_list.extend(device_list)
|
|
199
|
+
|
|
200
|
+
# get product_list
|
|
201
|
+
productIds = ",".join([item[CONF_PRODUCT_ID] for item in final_device_list])
|
|
202
|
+
product_list = await self.async_get_products(productIds)
|
|
203
|
+
|
|
204
|
+
for product in product_list:
|
|
205
|
+
for device in final_device_list:
|
|
206
|
+
if device[CONF_PRODUCT_ID] == product[CONF_ID]:
|
|
207
|
+
device[CONF_PRODUCT] = product
|
|
208
|
+
except Exception as e:
|
|
209
|
+
raise e
|
|
166
210
|
return final_device_list
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from enum import StrEnum
|
|
1
|
+
from enum import StrEnum,IntEnum
|
|
2
2
|
SUPPORTED_COUNTRYS = [
|
|
3
3
|
{"_id": "1-0", "id": "AL", "name": "Albania", "ext": "", "region": "EU"},
|
|
4
4
|
{
|
|
@@ -166,11 +166,17 @@ DEFAULT_COUNTRY_NAME = "United States"
|
|
|
166
166
|
CONF_APP_ID = "Appid"
|
|
167
167
|
CONF_TERMINAL = "Terminal"
|
|
168
168
|
CONF_LOGIN_RESPONSE = "login_response"
|
|
169
|
+
CONF_LOGIN_INFO = "login_info"
|
|
169
170
|
CONF_SELECTED_HOUSE = "selected_house"
|
|
170
171
|
CONF_DEVICE_LIST = "device_list"
|
|
171
172
|
CONF_PRODUCT_LIST = "product_list"
|
|
172
173
|
CONF_ACCESS_TOKEN = "accessToken"
|
|
173
174
|
CONF_REFRESH_TOKEN = "refreshToken"
|
|
175
|
+
CONF_TOKEN = "Token"
|
|
176
|
+
CONF_USERNAME = "username"
|
|
177
|
+
CONF_PASSWORD = "password"
|
|
178
|
+
CONF_REGION = "region"
|
|
179
|
+
CONF_COUNTRY = "country"
|
|
174
180
|
CONF_ID = "id"
|
|
175
181
|
CONF_NAME = "name"
|
|
176
182
|
CONF_PRODUCT_ID = "productId"
|
|
@@ -188,7 +194,7 @@ CONF_PROPERTIES = "properties"
|
|
|
188
194
|
CONF_MINVALUE = "minValue"
|
|
189
195
|
CONF_MAXVALUE = "maxValue"
|
|
190
196
|
CONF_IPADDRESS = "ipAddress"
|
|
191
|
-
|
|
197
|
+
CONF_CODE = "code"
|
|
192
198
|
class Identity(StrEnum):
|
|
193
199
|
"""Available entity identity."""
|
|
194
200
|
RGBW = "control.light.rgbw"
|
|
@@ -197,4 +203,9 @@ class Identity(StrEnum):
|
|
|
197
203
|
class Attribute(StrEnum):
|
|
198
204
|
"""Available entity attributes."""
|
|
199
205
|
RGBW = "rgbw"
|
|
200
|
-
CCT = "cct"
|
|
206
|
+
CCT = "cct"
|
|
207
|
+
|
|
208
|
+
class ServerErrorCode(IntEnum):
|
|
209
|
+
TOKEN_EXPIRED = 21026
|
|
210
|
+
LOGIN_INVALID = 21025
|
|
211
|
+
USER_PWD_INCORRECT = 560080
|
|
@@ -14,5 +14,9 @@ class InvalidHost(AidotError):
|
|
|
14
14
|
class AidotAuthTokenExpired(AidotError):
|
|
15
15
|
"""Authentication failed because token is invalid or expired."""
|
|
16
16
|
|
|
17
|
-
class
|
|
17
|
+
class AidotAuthFailed(AidotError):
|
|
18
18
|
"""Authentication failed because MFA verification code is required."""
|
|
19
|
+
|
|
20
|
+
class AidotUserOrPassIncorrect(AidotError):
|
|
21
|
+
"""The password or email address is incorrect."""
|
|
22
|
+
|
|
@@ -203,9 +203,10 @@ class Lan(object):
|
|
|
203
203
|
|
|
204
204
|
async def ping_task(self):
|
|
205
205
|
while True:
|
|
206
|
+
await asyncio.sleep(5) #加个延迟,不然遇到查状态和发ping同时出现状态回不来,暂时不知道什么原因
|
|
206
207
|
if await self.sendPingAction() == -1 :
|
|
207
208
|
return
|
|
208
|
-
await asyncio.sleep(
|
|
209
|
+
await asyncio.sleep(5)
|
|
209
210
|
|
|
210
211
|
def getOnOffAction(self,OnOff):
|
|
211
212
|
self._is_on = OnOff
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|