python-aidot 0.3.41__tar.gz → 0.3.43__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.3.41 → python_aidot-0.3.43}/PKG-INFO +1 -1
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/client.py +17 -4
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/const.py +5 -2
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/device_client.py +112 -49
- {python_aidot-0.3.41 → python_aidot-0.3.43}/python_aidot.egg-info/PKG-INFO +1 -1
- {python_aidot-0.3.41 → python_aidot-0.3.43}/setup.py +1 -1
- {python_aidot-0.3.41 → python_aidot-0.3.43}/LICENSE +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/README.md +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/__init__.py +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/aes_utils.py +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/discover.py +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/exceptions.py +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/aidot/login_const.py +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/python_aidot.egg-info/SOURCES.txt +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/python_aidot.egg-info/dependency_links.txt +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/python_aidot.egg-info/requires.txt +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/python_aidot.egg-info/top_level.txt +0 -0
- {python_aidot-0.3.41 → python_aidot-0.3.43}/setup.cfg +0 -0
|
@@ -32,6 +32,8 @@ from .const import (
|
|
|
32
32
|
CONF_USERNAME,
|
|
33
33
|
DEFAULT_COUNTRY_NAME,
|
|
34
34
|
SUPPORTED_COUNTRYS,
|
|
35
|
+
DEFAULT_COUNTRY_CODE,
|
|
36
|
+
CONF_IS_OWNER,
|
|
35
37
|
ServerErrorCode,
|
|
36
38
|
)
|
|
37
39
|
|
|
@@ -60,6 +62,7 @@ class AidotClient:
|
|
|
60
62
|
username: str = ""
|
|
61
63
|
password: str = ""
|
|
62
64
|
country_name: str = DEFAULT_COUNTRY_NAME
|
|
65
|
+
country_code: str = DEFAULT_COUNTRY_CODE
|
|
63
66
|
login_info: dict[str, Any] = {}
|
|
64
67
|
_device_clients: dict[str, DeviceClient]
|
|
65
68
|
_discover: Discover = None
|
|
@@ -67,18 +70,19 @@ class AidotClient:
|
|
|
67
70
|
def __init__(
|
|
68
71
|
self,
|
|
69
72
|
session: Optional[ClientSession],
|
|
70
|
-
|
|
73
|
+
country_code: str | None = None,
|
|
71
74
|
username: str | None = None,
|
|
72
75
|
password: str | None = None,
|
|
73
76
|
token: dict | None = None,
|
|
74
77
|
) -> None:
|
|
75
78
|
self.session = session
|
|
76
|
-
self.country_name = country_name
|
|
77
79
|
self.username = username
|
|
78
80
|
self.password = password
|
|
81
|
+
self.country_code = country_code
|
|
79
82
|
self._device_clients = {}
|
|
80
83
|
for item in SUPPORTED_COUNTRYS:
|
|
81
|
-
if item["
|
|
84
|
+
if item["id"] == self.country_code:
|
|
85
|
+
self.country_name = item["name"]
|
|
82
86
|
self._region = item["region"].lower()
|
|
83
87
|
self._base_url = f"https://prod-{self._region}-api.arnoo.com/v17"
|
|
84
88
|
break
|
|
@@ -102,8 +106,9 @@ class AidotClient:
|
|
|
102
106
|
"""Login the user input allows us to connect."""
|
|
103
107
|
url = f"{self._base_url}/users/loginWithFreeVerification"
|
|
104
108
|
headers = {CONF_APP_ID: APP_ID, CONF_TERMINAL: "app"}
|
|
109
|
+
# f"{region}:{self.country_name.strip()}",
|
|
105
110
|
data = {
|
|
106
|
-
"countryKey": "region:
|
|
111
|
+
"countryKey": f"region:{self.country_name.strip()}",
|
|
107
112
|
"username": self.username,
|
|
108
113
|
"password": rsa_password_encrypt(self.password),
|
|
109
114
|
"terminalId": "gvz3gjae10l4zii00t7y0",
|
|
@@ -206,6 +211,8 @@ class AidotClient:
|
|
|
206
211
|
try:
|
|
207
212
|
houses = await self.async_get_houses()
|
|
208
213
|
for house in houses:
|
|
214
|
+
if house.get(CONF_IS_OWNER) is False:
|
|
215
|
+
continue
|
|
209
216
|
# get device_list
|
|
210
217
|
device_list = await self.async_get_devices(house[CONF_ID])
|
|
211
218
|
if device_list:
|
|
@@ -235,6 +242,12 @@ class AidotClient:
|
|
|
235
242
|
device_client.update_ip_address(ip)
|
|
236
243
|
return device_client
|
|
237
244
|
|
|
245
|
+
async def remove_device_client(self, dev_id: str) -> None:
|
|
246
|
+
device_client: DeviceClient = self._device_clients.get(dev_id)
|
|
247
|
+
if device_client is not None:
|
|
248
|
+
await device_client.close()
|
|
249
|
+
del self._device_clients[dev_id]
|
|
250
|
+
|
|
238
251
|
def start_discover(self) -> None:
|
|
239
252
|
if self._discover is not None:
|
|
240
253
|
return
|
|
@@ -111,7 +111,7 @@ SUPPORTED_COUNTRYS = [
|
|
|
111
111
|
{"_id": "19-4", "id": "SR", "name": "Suriname", "ext": "", "region": "US"},
|
|
112
112
|
{"_id": "19-5", "id": "SE", "name": "Sweden", "ext": "", "region": "EU"},
|
|
113
113
|
{"_id": "19-6", "id": "SK", "name": "Slovakia", "ext": "", "region": "EU"},
|
|
114
|
-
{"_id": "19-7", "id": "RS
|
|
114
|
+
{"_id": "19-7", "id": "RS", "name": "Serbia", "ext": "", "region": "EU"},
|
|
115
115
|
{
|
|
116
116
|
"_id": "19-8",
|
|
117
117
|
"id": "KN",
|
|
@@ -161,9 +161,10 @@ SUPPORTED_COUNTRYS = [
|
|
|
161
161
|
{"_id": "22-2", "id": "VN", "name": "Vietnam", "ext": "", "region": "JP"},
|
|
162
162
|
{"_id": "23-0", "id": "YE", "name": "Yemen", "ext": "", "region": "JP"},
|
|
163
163
|
]
|
|
164
|
-
|
|
164
|
+
SUPPORTED_COUNTRY_CODES = [item["id"] for item in SUPPORTED_COUNTRYS]
|
|
165
165
|
SUPPORTED_COUNTRY_NAMES = [item["name"] for item in SUPPORTED_COUNTRYS]
|
|
166
166
|
DEFAULT_COUNTRY_NAME = "United States"
|
|
167
|
+
DEFAULT_COUNTRY_CODE = "US"
|
|
167
168
|
CONF_APP_ID = "Appid"
|
|
168
169
|
CONF_TERMINAL = "Terminal"
|
|
169
170
|
CONF_LOGIN_RESPONSE = "login_response"
|
|
@@ -203,6 +204,8 @@ CONF_ON_OFF = "OnOff"
|
|
|
203
204
|
CONF_DIMMING = "Dimming"
|
|
204
205
|
CONF_RGBW = "RGBW"
|
|
205
206
|
CONF_CCT = "CCT"
|
|
207
|
+
CONF_ACK = "ack"
|
|
208
|
+
CONF_IS_OWNER = "isOwner"
|
|
206
209
|
|
|
207
210
|
|
|
208
211
|
class Identity(StrEnum):
|
|
@@ -33,6 +33,8 @@ from .const import (
|
|
|
33
33
|
CONF_PROPERTIES,
|
|
34
34
|
CONF_RGBW,
|
|
35
35
|
CONF_SERVICE_MODULES,
|
|
36
|
+
CONF_ACK,
|
|
37
|
+
CONF_CODE,
|
|
36
38
|
Identity,
|
|
37
39
|
)
|
|
38
40
|
|
|
@@ -102,10 +104,10 @@ class DeviceClient(object):
|
|
|
102
104
|
_connect_and_login: bool = False
|
|
103
105
|
_connecting: bool = False
|
|
104
106
|
_simpleVersion: str = ""
|
|
105
|
-
_ip_address: str
|
|
107
|
+
_ip_address: str = None
|
|
106
108
|
device_id: str
|
|
107
109
|
_is_close: bool = False
|
|
108
|
-
|
|
110
|
+
_status_fresh_cb: Any = None
|
|
109
111
|
@property
|
|
110
112
|
def connect_and_login(self) -> bool:
|
|
111
113
|
return self._connect_and_login
|
|
@@ -132,6 +134,7 @@ class DeviceClient(object):
|
|
|
132
134
|
self._simpleVersion = device.get("simpleVersion")
|
|
133
135
|
|
|
134
136
|
async def connect(self, ip_address) -> None:
|
|
137
|
+
_LOGGER.info(f"connect device : {ip_address}")
|
|
135
138
|
self.reader = self.writer = None
|
|
136
139
|
self._connecting = True
|
|
137
140
|
try:
|
|
@@ -147,8 +150,12 @@ class DeviceClient(object):
|
|
|
147
150
|
self._connecting = False
|
|
148
151
|
|
|
149
152
|
def update_ip_address(self, ip: str) -> None:
|
|
153
|
+
if ip is None:
|
|
154
|
+
return
|
|
150
155
|
self._ip_address = ip
|
|
151
|
-
|
|
156
|
+
if self._connecting is not True and self._connect_and_login is not True:
|
|
157
|
+
asyncio.get_running_loop().create_task(self.async_login())
|
|
158
|
+
|
|
152
159
|
async def async_login(self) -> None:
|
|
153
160
|
if self._ip_address is None:
|
|
154
161
|
return
|
|
@@ -198,57 +205,111 @@ class DeviceClient(object):
|
|
|
198
205
|
data_len = len(data)
|
|
199
206
|
if data_len <= 0:
|
|
200
207
|
return
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
|
|
211
|
+
encrypted_data = data[8:]
|
|
212
|
+
if self.aes_key is not None:
|
|
213
|
+
decrypted_data = aes_decrypt(encrypted_data, self.aes_key)
|
|
214
|
+
else:
|
|
215
|
+
decrypted_data = encrypted_data
|
|
201
216
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
217
|
+
json_data = json.loads(decrypted_data)
|
|
218
|
+
code = json_data[CONF_ACK][CONF_CODE]
|
|
219
|
+
if code != 200:
|
|
220
|
+
# 登录失败
|
|
221
|
+
_LOGGER.error(f"{self.device_id} login error, code: {code}")
|
|
222
|
+
await self.reset()
|
|
223
|
+
return
|
|
208
224
|
|
|
209
|
-
|
|
225
|
+
self.ascNumber = json_data[CONF_PAYLOAD][CONF_ASCNUMBER]
|
|
226
|
+
self.ascNumber += 1
|
|
227
|
+
self.status.online = True
|
|
228
|
+
asyncio.get_running_loop().create_task(self.reveive_data())
|
|
229
|
+
_LOGGER.info(f"connect device success: {self._ip_address}")
|
|
230
|
+
await self.send_action({}, "getDevAttrReq")
|
|
231
|
+
except Exception as e:
|
|
232
|
+
_LOGGER.error(f"connect device error : {e}")
|
|
233
|
+
return
|
|
210
234
|
|
|
211
|
-
|
|
212
|
-
self.ascNumber += 1
|
|
213
|
-
self.status.online = True
|
|
214
|
-
await self.send_action({}, "getDevAttrReq")
|
|
235
|
+
|
|
215
236
|
|
|
237
|
+
async def reveive_data(self) -> None:
|
|
238
|
+
while True:
|
|
239
|
+
try:
|
|
240
|
+
data = await self.reader.read(1024)
|
|
241
|
+
except (BrokenPipeError, ConnectionResetError) as e:
|
|
242
|
+
_LOGGER.error(f"{self.device_id} read status error {e}")
|
|
243
|
+
await self.reset()
|
|
244
|
+
self.status.online = False
|
|
245
|
+
return
|
|
246
|
+
except Exception as e:
|
|
247
|
+
_LOGGER.error(f"recv data error {e}")
|
|
248
|
+
return
|
|
249
|
+
data_len = len(data)
|
|
250
|
+
if data_len <= 0:
|
|
251
|
+
_LOGGER.error("recv data error len, exit socket")
|
|
252
|
+
await self.reset()
|
|
253
|
+
self.status.online = False
|
|
254
|
+
return
|
|
255
|
+
try:
|
|
256
|
+
magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
|
|
257
|
+
decrypted_data = aes_decrypt(data[8:], self.aes_key)
|
|
258
|
+
json_data = json.loads(decrypted_data)
|
|
259
|
+
except Exception as e:
|
|
260
|
+
_LOGGER.error(f"recv json error : {e}")
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
if "service" in json_data:
|
|
264
|
+
if "test" == json_data["service"]:
|
|
265
|
+
self.ping_count = 0
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
payload = json_data.get(CONF_PAYLOAD)
|
|
269
|
+
if payload is not None:
|
|
270
|
+
self.ascNumber = payload.get(CONF_ASCNUMBER)
|
|
271
|
+
self.status.update(payload.get(CONF_ATTR))
|
|
272
|
+
# _LOGGER.info(f"recv status : {payload}")
|
|
273
|
+
if self._status_fresh_cb:
|
|
274
|
+
self._status_fresh_cb(self.status)
|
|
275
|
+
def set_status_fresh_cb(self, callback) -> None:
|
|
276
|
+
self._status_fresh_cb = callback
|
|
216
277
|
async def read_status(self) -> DeviceStatusData:
|
|
217
|
-
if self._connect_and_login is False:
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
try:
|
|
221
|
-
|
|
222
|
-
except (BrokenPipeError, ConnectionResetError) as e:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
except Exception as e:
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
data_len = len(data)
|
|
231
|
-
if data_len <= 0:
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
try:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
except Exception as e:
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if "service" in json_data:
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
payload = json_data.get(CONF_PAYLOAD)
|
|
249
|
-
if payload is not None:
|
|
250
|
-
|
|
251
|
-
|
|
278
|
+
# if self._connect_and_login is False:
|
|
279
|
+
# await asyncio.sleep(2)
|
|
280
|
+
# raise AidotNotLogin
|
|
281
|
+
# try:
|
|
282
|
+
# data = await self.reader.read(1024)
|
|
283
|
+
# except (BrokenPipeError, ConnectionResetError) as e:
|
|
284
|
+
# _LOGGER.error(f"{self.device_id} read status error {e}")
|
|
285
|
+
# await self.reset()
|
|
286
|
+
# self.status.online = False
|
|
287
|
+
# return self.status
|
|
288
|
+
# except Exception as e:
|
|
289
|
+
# _LOGGER.error(f"recv data error {e}")
|
|
290
|
+
# return self.status
|
|
291
|
+
# data_len = len(data)
|
|
292
|
+
# if data_len <= 0:
|
|
293
|
+
# _LOGGER.error("recv data error len")
|
|
294
|
+
# await self.reset()
|
|
295
|
+
# self.status.online = False
|
|
296
|
+
# return self.status
|
|
297
|
+
# try:
|
|
298
|
+
# magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
|
|
299
|
+
# decrypted_data = aes_decrypt(data[8:], self.aes_key)
|
|
300
|
+
# json_data = json.loads(decrypted_data)
|
|
301
|
+
# except Exception as e:
|
|
302
|
+
# _LOGGER.error(f"recv json error : {e}")
|
|
303
|
+
# return await self.read_status()
|
|
304
|
+
|
|
305
|
+
# if "service" in json_data:
|
|
306
|
+
# if "test" == json_data["service"]:
|
|
307
|
+
# self.ping_count = 0
|
|
308
|
+
# return await self.read_status()
|
|
309
|
+
# payload = json_data.get(CONF_PAYLOAD)
|
|
310
|
+
# if payload is not None:
|
|
311
|
+
# self.ascNumber = payload.get(CONF_ASCNUMBER)
|
|
312
|
+
# self.status.update(payload.get(CONF_ATTR))
|
|
252
313
|
return self.status
|
|
253
314
|
|
|
254
315
|
async def ping_task(self) -> None:
|
|
@@ -260,6 +321,8 @@ class DeviceClient(object):
|
|
|
260
321
|
await asyncio.sleep(5)
|
|
261
322
|
|
|
262
323
|
async def send_dev_attr(self, dev_attr) -> None:
|
|
324
|
+
if not self._connect_and_login:
|
|
325
|
+
raise ConnectionError('Device offline')
|
|
263
326
|
await self.send_action(dev_attr, "setDevAttrReq")
|
|
264
327
|
|
|
265
328
|
async def async_turn_off(self) -> None:
|
|
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
|