python-aidot 0.3.3__tar.gz → 0.3.41__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.3 → python_aidot-0.3.41}/PKG-INFO +1 -1
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/client.py +21 -15
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/device_client.py +16 -15
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/discover.py +9 -8
- {python_aidot-0.3.3 → python_aidot-0.3.41}/python_aidot.egg-info/PKG-INFO +1 -1
- {python_aidot-0.3.3 → python_aidot-0.3.41}/setup.py +1 -1
- {python_aidot-0.3.3 → python_aidot-0.3.41}/LICENSE +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/README.md +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/__init__.py +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/aes_utils.py +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/const.py +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/exceptions.py +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/aidot/login_const.py +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/python_aidot.egg-info/SOURCES.txt +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/python_aidot.egg-info/dependency_links.txt +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/python_aidot.egg-info/requires.txt +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/python_aidot.egg-info/top_level.txt +0 -0
- {python_aidot-0.3.3 → python_aidot-0.3.41}/setup.cfg +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""The aidot integration."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
3
4
|
import logging
|
|
4
5
|
import base64
|
|
5
6
|
import aiohttp
|
|
@@ -37,7 +38,7 @@ from .const import (
|
|
|
37
38
|
_LOGGER = logging.getLogger(__name__)
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
def rsa_password_encrypt(message: str):
|
|
41
|
+
def rsa_password_encrypt(message: str) -> str:
|
|
41
42
|
"""Get password rsa encrypt."""
|
|
42
43
|
public_key = serialization.load_pem_public_key(
|
|
43
44
|
PUBLIC_KEY_PEM, backend=default_backend()
|
|
@@ -60,7 +61,7 @@ class AidotClient:
|
|
|
60
61
|
password: str = ""
|
|
61
62
|
country_name: str = DEFAULT_COUNTRY_NAME
|
|
62
63
|
login_info: dict[str, Any] = {}
|
|
63
|
-
_device_clients: dict[str
|
|
64
|
+
_device_clients: dict[str, DeviceClient]
|
|
64
65
|
_discover: Discover = None
|
|
65
66
|
|
|
66
67
|
def __init__(
|
|
@@ -75,7 +76,6 @@ class AidotClient:
|
|
|
75
76
|
self.country_name = country_name
|
|
76
77
|
self.username = username
|
|
77
78
|
self.password = password
|
|
78
|
-
self.login_info = token.copy()
|
|
79
79
|
self._device_clients = {}
|
|
80
80
|
for item in SUPPORTED_COUNTRYS:
|
|
81
81
|
if item["name"] == self.country_name:
|
|
@@ -83,21 +83,22 @@ class AidotClient:
|
|
|
83
83
|
self._base_url = f"https://prod-{self._region}-api.arnoo.com/v17"
|
|
84
84
|
break
|
|
85
85
|
if token is not None:
|
|
86
|
+
self.login_info = token.copy()
|
|
86
87
|
self.username = token[CONF_USERNAME]
|
|
87
88
|
self.password = token[CONF_PASSWORD]
|
|
88
89
|
self._region = token[CONF_REGION]
|
|
89
90
|
self.country_name = token[CONF_COUNTRY]
|
|
90
91
|
|
|
91
|
-
def set_token_fresh_cb(self, callback):
|
|
92
|
+
def set_token_fresh_cb(self, callback) -> None:
|
|
92
93
|
self._token_fresh_cb = callback
|
|
93
94
|
|
|
94
95
|
def get_identifier(self) -> str:
|
|
95
96
|
return f"{self._region}-{self.username}"
|
|
96
97
|
|
|
97
|
-
def update_password(self, password: str):
|
|
98
|
+
def update_password(self, password: str) -> None:
|
|
98
99
|
self.password = password
|
|
99
100
|
|
|
100
|
-
async def async_post_login(self):
|
|
101
|
+
async def async_post_login(self) -> dict[str, Any]:
|
|
101
102
|
"""Login the user input allows us to connect."""
|
|
102
103
|
url = f"{self._base_url}/users/loginWithFreeVerification"
|
|
103
104
|
headers = {CONF_APP_ID: APP_ID, CONF_TERMINAL: "app"}
|
|
@@ -126,7 +127,7 @@ class AidotClient:
|
|
|
126
127
|
raise AidotUserOrPassIncorrect
|
|
127
128
|
raise Exception
|
|
128
129
|
|
|
129
|
-
async def async_refresh_token(self):
|
|
130
|
+
async def async_refresh_token(self) -> dict[str, Any]:
|
|
130
131
|
url = f"{self._base_url}/users/refreshToken"
|
|
131
132
|
headers = {CONF_APP_ID: APP_ID, CONF_TERMINAL: "app"}
|
|
132
133
|
data = {
|
|
@@ -150,7 +151,9 @@ class AidotClient:
|
|
|
150
151
|
raise AidotAuthFailed
|
|
151
152
|
return None
|
|
152
153
|
|
|
153
|
-
async def async_session_get(
|
|
154
|
+
async def async_session_get(
|
|
155
|
+
self, params: str, headers: str | None = None
|
|
156
|
+
) -> dict[str, Any]:
|
|
154
157
|
url = f"{self._base_url}{params}"
|
|
155
158
|
token = self.login_info[CONF_ACCESS_TOKEN]
|
|
156
159
|
if token is None:
|
|
@@ -183,22 +186,22 @@ class AidotClient:
|
|
|
183
186
|
raise AidotAuthFailed
|
|
184
187
|
return aiohttp.ClientError
|
|
185
188
|
|
|
186
|
-
async def async_get_products(self, product_ids: str):
|
|
189
|
+
async def async_get_products(self, product_ids: str) -> list[dict[str, Any]]:
|
|
187
190
|
"""Get device list."""
|
|
188
191
|
params = f"/products/{product_ids}"
|
|
189
192
|
return await self.async_session_get(params)
|
|
190
193
|
|
|
191
|
-
async def async_get_devices(self, house_id: str):
|
|
194
|
+
async def async_get_devices(self, house_id: str) -> list[dict[str, Any]]:
|
|
192
195
|
"""Get device list."""
|
|
193
196
|
params = f"/devices?houseId={house_id}"
|
|
194
197
|
return await self.async_session_get(params)
|
|
195
198
|
|
|
196
|
-
async def async_get_houses(self):
|
|
199
|
+
async def async_get_houses(self) -> list[dict[str, Any]]:
|
|
197
200
|
"""Get house list."""
|
|
198
201
|
params = "/houses"
|
|
199
202
|
return await self.async_session_get(params)
|
|
200
203
|
|
|
201
|
-
async def async_get_all_device(self):
|
|
204
|
+
async def async_get_all_device(self) -> dict[str, Any]:
|
|
202
205
|
final_device_list: list[dict[str, Any]] = []
|
|
203
206
|
try:
|
|
204
207
|
houses = await self.async_get_houses()
|
|
@@ -220,18 +223,19 @@ class AidotClient:
|
|
|
220
223
|
raise e
|
|
221
224
|
return {CONF_DEVICE_LIST: final_device_list}
|
|
222
225
|
|
|
223
|
-
def get_device_client(self, device: dict[str
|
|
226
|
+
def get_device_client(self, device: dict[str, Any]) -> DeviceClient:
|
|
224
227
|
device_id = device.get(CONF_ID)
|
|
225
228
|
device_client: DeviceClient = self._device_clients.get(device_id)
|
|
226
229
|
if device_client is None:
|
|
227
230
|
device_client = DeviceClient(device, self.login_info)
|
|
228
231
|
self._device_clients[device_id] = device_client
|
|
232
|
+
asyncio.get_running_loop().create_task(device_client.ping_task())
|
|
229
233
|
if self._discover is not None:
|
|
230
234
|
ip = self._discover.discovered_device.get(device_id)
|
|
231
235
|
device_client.update_ip_address(ip)
|
|
232
236
|
return device_client
|
|
233
237
|
|
|
234
|
-
|
|
238
|
+
def start_discover(self) -> None:
|
|
235
239
|
if self._discover is not None:
|
|
236
240
|
return
|
|
237
241
|
|
|
@@ -242,7 +246,7 @@ class AidotClient:
|
|
|
242
246
|
device_client.update_ip_address(device_ip)
|
|
243
247
|
|
|
244
248
|
self._discover = Discover(self.login_info, _discover_callback)
|
|
245
|
-
|
|
249
|
+
asyncio.get_running_loop().create_task(self._discover.repeat_broadcast())
|
|
246
250
|
|
|
247
251
|
def stop_discover(self) -> None:
|
|
248
252
|
self._discover.close()
|
|
@@ -250,4 +254,6 @@ class AidotClient:
|
|
|
250
254
|
|
|
251
255
|
def cleanup(self) -> None:
|
|
252
256
|
self.stop_discover()
|
|
257
|
+
for client in self._device_clients.values():
|
|
258
|
+
asyncio.get_running_loop().create_task(client.close())
|
|
253
259
|
self._device_clients.clear()
|
|
@@ -78,7 +78,7 @@ class DeviceInformation:
|
|
|
78
78
|
name: str
|
|
79
79
|
hw_version: str
|
|
80
80
|
|
|
81
|
-
def __init__(self, device: dict[str
|
|
81
|
+
def __init__(self, device: dict[str, Any]) -> None:
|
|
82
82
|
self.dev_id = device.get(CONF_ID)
|
|
83
83
|
self.mac = device.get(CONF_MAC) if device.get(CONF_MAC) is not None else ""
|
|
84
84
|
self.model_id = device.get(CONF_MODEL_ID)
|
|
@@ -114,7 +114,7 @@ class DeviceClient(object):
|
|
|
114
114
|
def connecting(self) -> bool:
|
|
115
115
|
return self._connecting
|
|
116
116
|
|
|
117
|
-
def __init__(self, device: dict, user_info: dict) -> None:
|
|
117
|
+
def __init__(self, device: dict[str, Any], user_info: dict[str, Any]) -> None:
|
|
118
118
|
self.ping_count = 0
|
|
119
119
|
self.status = DeviceStatusData()
|
|
120
120
|
self.info = DeviceInformation(device)
|
|
@@ -131,7 +131,7 @@ class DeviceClient(object):
|
|
|
131
131
|
self.device_id = device.get(CONF_ID)
|
|
132
132
|
self._simpleVersion = device.get("simpleVersion")
|
|
133
133
|
|
|
134
|
-
async def connect(self, ip_address):
|
|
134
|
+
async def connect(self, ip_address) -> None:
|
|
135
135
|
self.reader = self.writer = None
|
|
136
136
|
self._connecting = True
|
|
137
137
|
try:
|
|
@@ -155,7 +155,7 @@ class DeviceClient(object):
|
|
|
155
155
|
if self._connecting is not True and self._connect_and_login is not True:
|
|
156
156
|
await self.connect(self._ip_address)
|
|
157
157
|
|
|
158
|
-
def
|
|
158
|
+
def get_send_packet(self, message, msgtype):
|
|
159
159
|
magic = struct.pack(">H", 0x1EED)
|
|
160
160
|
_msgtype = struct.pack(">h", msgtype)
|
|
161
161
|
|
|
@@ -169,7 +169,7 @@ class DeviceClient(object):
|
|
|
169
169
|
|
|
170
170
|
return packet
|
|
171
171
|
|
|
172
|
-
async def login(self):
|
|
172
|
+
async def login(self) -> None:
|
|
173
173
|
login_seq = str(int(time.time() * 1000) + self._login_uuid)[-9:]
|
|
174
174
|
self._login_uuid += 1
|
|
175
175
|
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
|
|
@@ -187,7 +187,7 @@ class DeviceClient(object):
|
|
|
187
187
|
},
|
|
188
188
|
}
|
|
189
189
|
try:
|
|
190
|
-
self.writer.write(self.
|
|
190
|
+
self.writer.write(self.get_send_packet(json.dumps(message).encode(), 1))
|
|
191
191
|
await self.writer.drain()
|
|
192
192
|
data = await self.reader.read(1024)
|
|
193
193
|
except (BrokenPipeError, ConnectionResetError) as e:
|
|
@@ -213,7 +213,7 @@ class DeviceClient(object):
|
|
|
213
213
|
self.status.online = True
|
|
214
214
|
await self.send_action({}, "getDevAttrReq")
|
|
215
215
|
|
|
216
|
-
async def read_status(self):
|
|
216
|
+
async def read_status(self) -> DeviceStatusData:
|
|
217
217
|
if self._connect_and_login is False:
|
|
218
218
|
await asyncio.sleep(2)
|
|
219
219
|
raise AidotNotLogin
|
|
@@ -251,7 +251,7 @@ class DeviceClient(object):
|
|
|
251
251
|
self.status.update(payload.get(CONF_ATTR))
|
|
252
252
|
return self.status
|
|
253
253
|
|
|
254
|
-
async def ping_task(self):
|
|
254
|
+
async def ping_task(self) -> None:
|
|
255
255
|
while True:
|
|
256
256
|
if self._is_close:
|
|
257
257
|
return
|
|
@@ -259,7 +259,7 @@ class DeviceClient(object):
|
|
|
259
259
|
await self.send_ping_action()
|
|
260
260
|
await asyncio.sleep(5)
|
|
261
261
|
|
|
262
|
-
async def send_dev_attr(self, dev_attr):
|
|
262
|
+
async def send_dev_attr(self, dev_attr) -> None:
|
|
263
263
|
await self.send_action(dev_attr, "setDevAttrReq")
|
|
264
264
|
|
|
265
265
|
async def async_turn_off(self) -> None:
|
|
@@ -279,7 +279,7 @@ class DeviceClient(object):
|
|
|
279
279
|
async def async_set_cct(self, cct: int) -> None:
|
|
280
280
|
await self.send_dev_attr({CONF_CCT: cct})
|
|
281
281
|
|
|
282
|
-
async def send_action(self, attr, method):
|
|
282
|
+
async def send_action(self, attr, method) -> None:
|
|
283
283
|
current_timestamp_milliseconds = int(time.time() * 1000)
|
|
284
284
|
self.seq_num += 1
|
|
285
285
|
seq = "ha93" + str(self.seq_num).zfill(5)
|
|
@@ -321,7 +321,7 @@ class DeviceClient(object):
|
|
|
321
321
|
}
|
|
322
322
|
|
|
323
323
|
try:
|
|
324
|
-
self.writer.write(self.
|
|
324
|
+
self.writer.write(self.get_send_packet(json.dumps(action).encode(), 1))
|
|
325
325
|
await self.writer.drain()
|
|
326
326
|
except (BrokenPipeError, ConnectionResetError) as e:
|
|
327
327
|
_LOGGER.error(f"{self.device_id} send action error {e}")
|
|
@@ -329,7 +329,7 @@ class DeviceClient(object):
|
|
|
329
329
|
except Exception as e:
|
|
330
330
|
_LOGGER.error(f"{self.device_id} send action error {e}")
|
|
331
331
|
|
|
332
|
-
async def send_ping_action(self):
|
|
332
|
+
async def send_ping_action(self) -> int:
|
|
333
333
|
ping = {
|
|
334
334
|
"service": "test",
|
|
335
335
|
"method": "pingreq",
|
|
@@ -346,7 +346,7 @@ class DeviceClient(object):
|
|
|
346
346
|
return -1
|
|
347
347
|
if self._connect_and_login is False:
|
|
348
348
|
return -1
|
|
349
|
-
self.writer.write(self.
|
|
349
|
+
self.writer.write(self.get_send_packet(json.dumps(ping).encode(), 2))
|
|
350
350
|
await self.writer.drain()
|
|
351
351
|
self.ping_count += 1
|
|
352
352
|
return 1
|
|
@@ -355,7 +355,7 @@ class DeviceClient(object):
|
|
|
355
355
|
await self.reset()
|
|
356
356
|
return -1
|
|
357
357
|
|
|
358
|
-
async def reset(self):
|
|
358
|
+
async def reset(self) -> None:
|
|
359
359
|
try:
|
|
360
360
|
if self.writer:
|
|
361
361
|
self.writer.close()
|
|
@@ -366,6 +366,7 @@ class DeviceClient(object):
|
|
|
366
366
|
self.status.online = False
|
|
367
367
|
self.ping_count = 0
|
|
368
368
|
|
|
369
|
-
async def close(self):
|
|
369
|
+
async def close(self) -> None:
|
|
370
370
|
self._is_close = True
|
|
371
371
|
await self.reset()
|
|
372
|
+
_LOGGER.info(f"{self.device_id} connect close by user")
|
|
@@ -12,10 +12,11 @@ from .exceptions import AidotOSError
|
|
|
12
12
|
_LOGGER = logging.getLogger(__name__)
|
|
13
13
|
_DISCOVER_TIME = 5
|
|
14
14
|
|
|
15
|
+
|
|
15
16
|
class BroadcastProtocol:
|
|
16
17
|
_is_closed = False
|
|
17
18
|
|
|
18
|
-
def __init__(self, callback, user_id):
|
|
19
|
+
def __init__(self, callback, user_id) -> None:
|
|
19
20
|
self.aes_key = bytearray(32)
|
|
20
21
|
key_string = "T54uednca587"
|
|
21
22
|
key_bytes = key_string.encode()
|
|
@@ -24,7 +25,7 @@ class BroadcastProtocol:
|
|
|
24
25
|
self._discover_cb = callback
|
|
25
26
|
self.user_id = user_id
|
|
26
27
|
|
|
27
|
-
def connection_made(self, transport):
|
|
28
|
+
def connection_made(self, transport) -> None:
|
|
28
29
|
self.transport = transport
|
|
29
30
|
sock = transport.get_extra_info("socket")
|
|
30
31
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
|
@@ -54,7 +55,7 @@ class BroadcastProtocol:
|
|
|
54
55
|
except Exception as error:
|
|
55
56
|
_LOGGER.error(f"{self.user_id}:Connection lost due to error: {error}")
|
|
56
57
|
|
|
57
|
-
def datagram_received(self, data, addr):
|
|
58
|
+
def datagram_received(self, data, addr) -> None:
|
|
58
59
|
data_str = aes_decrypt(data, self.aes_key)
|
|
59
60
|
data_json = json.loads(data_str)
|
|
60
61
|
if "payload" in data_json:
|
|
@@ -63,7 +64,7 @@ class BroadcastProtocol:
|
|
|
63
64
|
if self._discover_cb:
|
|
64
65
|
self._discover_cb(devId, {CONF_IPADDRESS: addr[0]})
|
|
65
66
|
|
|
66
|
-
def error_received(self, exc):
|
|
67
|
+
def error_received(self, exc) -> None:
|
|
67
68
|
_LOGGER.error(f"{self.user_id}:Error occurred: {exc}")
|
|
68
69
|
|
|
69
70
|
def close(self) -> None:
|
|
@@ -72,7 +73,7 @@ class BroadcastProtocol:
|
|
|
72
73
|
except Exception as error:
|
|
73
74
|
_LOGGER.error(f"Connection lost due to error: {error}")
|
|
74
75
|
|
|
75
|
-
def connection_lost(self, exc):
|
|
76
|
+
def connection_lost(self, exc) -> None:
|
|
76
77
|
self._is_closed = True
|
|
77
78
|
if exc:
|
|
78
79
|
_LOGGER.error(f"{self.user_id}:Connection lost due to error: {exc}")
|
|
@@ -81,9 +82,9 @@ class BroadcastProtocol:
|
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
class Discover:
|
|
84
|
-
_login_info: dict[str
|
|
85
|
+
_login_info: dict[str, Any] = None
|
|
85
86
|
_broadcast_protocol: BroadcastProtocol = None
|
|
86
|
-
discovered_device: dict[str
|
|
87
|
+
discovered_device: dict[str, str]
|
|
87
88
|
_is_close: bool = False
|
|
88
89
|
|
|
89
90
|
def __init__(self, login_info, callback):
|
|
@@ -120,7 +121,7 @@ class Discover:
|
|
|
120
121
|
if self._is_close is True:
|
|
121
122
|
return
|
|
122
123
|
|
|
123
|
-
async def fetch_devices_info(self) -> dict[str
|
|
124
|
+
async def fetch_devices_info(self) -> dict[str, str]:
|
|
124
125
|
self.try_create_broadcast()
|
|
125
126
|
self._broadcast_protocol.send_broadcast()
|
|
126
127
|
await asyncio.sleep(2)
|
|
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
|