python-aidot 0.3.4__tar.gz → 0.3.42__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.2
2
2
  Name: python-aidot
3
- Version: 0.3.4
3
+ Version: 0.3.42
4
4
  Summary: aidot control wifi lights
5
5
  Home-page: https://github.com/Aidot-Development-Team/python-aidot
6
6
  Author: aidotdev2024
@@ -38,7 +38,7 @@ from .const import (
38
38
  _LOGGER = logging.getLogger(__name__)
39
39
 
40
40
 
41
- def rsa_password_encrypt(message: str):
41
+ def rsa_password_encrypt(message: str) -> str:
42
42
  """Get password rsa encrypt."""
43
43
  public_key = serialization.load_pem_public_key(
44
44
  PUBLIC_KEY_PEM, backend=default_backend()
@@ -61,7 +61,7 @@ class AidotClient:
61
61
  password: str = ""
62
62
  country_name: str = DEFAULT_COUNTRY_NAME
63
63
  login_info: dict[str, Any] = {}
64
- _device_clients: dict[str:DeviceClient]
64
+ _device_clients: dict[str, DeviceClient]
65
65
  _discover: Discover = None
66
66
 
67
67
  def __init__(
@@ -89,16 +89,16 @@ class AidotClient:
89
89
  self._region = token[CONF_REGION]
90
90
  self.country_name = token[CONF_COUNTRY]
91
91
 
92
- def set_token_fresh_cb(self, callback):
92
+ def set_token_fresh_cb(self, callback) -> None:
93
93
  self._token_fresh_cb = callback
94
94
 
95
95
  def get_identifier(self) -> str:
96
96
  return f"{self._region}-{self.username}"
97
97
 
98
- def update_password(self, password: str):
98
+ def update_password(self, password: str) -> None:
99
99
  self.password = password
100
100
 
101
- async def async_post_login(self):
101
+ async def async_post_login(self) -> dict[str, Any]:
102
102
  """Login the user input allows us to connect."""
103
103
  url = f"{self._base_url}/users/loginWithFreeVerification"
104
104
  headers = {CONF_APP_ID: APP_ID, CONF_TERMINAL: "app"}
@@ -127,7 +127,7 @@ class AidotClient:
127
127
  raise AidotUserOrPassIncorrect
128
128
  raise Exception
129
129
 
130
- async def async_refresh_token(self):
130
+ async def async_refresh_token(self) -> dict[str, Any]:
131
131
  url = f"{self._base_url}/users/refreshToken"
132
132
  headers = {CONF_APP_ID: APP_ID, CONF_TERMINAL: "app"}
133
133
  data = {
@@ -151,7 +151,9 @@ class AidotClient:
151
151
  raise AidotAuthFailed
152
152
  return None
153
153
 
154
- async def async_session_get(self, params: str, headers: str | None = None):
154
+ async def async_session_get(
155
+ self, params: str, headers: str | None = None
156
+ ) -> dict[str, Any]:
155
157
  url = f"{self._base_url}{params}"
156
158
  token = self.login_info[CONF_ACCESS_TOKEN]
157
159
  if token is None:
@@ -184,22 +186,22 @@ class AidotClient:
184
186
  raise AidotAuthFailed
185
187
  return aiohttp.ClientError
186
188
 
187
- async def async_get_products(self, product_ids: str):
189
+ async def async_get_products(self, product_ids: str) -> list[dict[str, Any]]:
188
190
  """Get device list."""
189
191
  params = f"/products/{product_ids}"
190
192
  return await self.async_session_get(params)
191
193
 
192
- async def async_get_devices(self, house_id: str):
194
+ async def async_get_devices(self, house_id: str) -> list[dict[str, Any]]:
193
195
  """Get device list."""
194
196
  params = f"/devices?houseId={house_id}"
195
197
  return await self.async_session_get(params)
196
198
 
197
- async def async_get_houses(self):
199
+ async def async_get_houses(self) -> list[dict[str, Any]]:
198
200
  """Get house list."""
199
201
  params = "/houses"
200
202
  return await self.async_session_get(params)
201
203
 
202
- async def async_get_all_device(self):
204
+ async def async_get_all_device(self) -> dict[str, Any]:
203
205
  final_device_list: list[dict[str, Any]] = []
204
206
  try:
205
207
  houses = await self.async_get_houses()
@@ -221,7 +223,7 @@ class AidotClient:
221
223
  raise e
222
224
  return {CONF_DEVICE_LIST: final_device_list}
223
225
 
224
- def get_device_client(self, device: dict[str:Any]) -> DeviceClient:
226
+ def get_device_client(self, device: dict[str, Any]) -> DeviceClient:
225
227
  device_id = device.get(CONF_ID)
226
228
  device_client: DeviceClient = self._device_clients.get(device_id)
227
229
  if device_client is None:
@@ -233,6 +235,12 @@ class AidotClient:
233
235
  device_client.update_ip_address(ip)
234
236
  return device_client
235
237
 
238
+ async def remove_device_client(self, dev_id: str) -> None:
239
+ device_client: DeviceClient = self._device_clients.get(dev_id)
240
+ if device_client is not None:
241
+ await device_client.close()
242
+ del self._device_clients[dev_id]
243
+
236
244
  def start_discover(self) -> None:
237
245
  if self._discover is not None:
238
246
  return
@@ -78,7 +78,7 @@ class DeviceInformation:
78
78
  name: str
79
79
  hw_version: str
80
80
 
81
- def __init__(self, device: dict[str:Any]):
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)
@@ -102,10 +102,10 @@ class DeviceClient(object):
102
102
  _connect_and_login: bool = False
103
103
  _connecting: bool = False
104
104
  _simpleVersion: str = ""
105
- _ip_address: str
105
+ _ip_address: str = None
106
106
  device_id: str
107
107
  _is_close: bool = False
108
-
108
+ _status_fresh_cb: Any = None
109
109
  @property
110
110
  def connect_and_login(self) -> bool:
111
111
  return self._connect_and_login
@@ -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,8 @@ 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
+ _LOGGER.info(f"connect device : {ip_address}")
135
136
  self.reader = self.writer = None
136
137
  self._connecting = True
137
138
  try:
@@ -147,15 +148,19 @@ class DeviceClient(object):
147
148
  self._connecting = False
148
149
 
149
150
  def update_ip_address(self, ip: str) -> None:
151
+ if ip is None:
152
+ return
150
153
  self._ip_address = ip
151
-
154
+ if self._connecting is not True and self._connect_and_login is not True:
155
+ asyncio.get_running_loop().create_task(self.async_login())
156
+
152
157
  async def async_login(self) -> None:
153
158
  if self._ip_address is None:
154
159
  return
155
160
  if self._connecting is not True and self._connect_and_login is not True:
156
161
  await self.connect(self._ip_address)
157
162
 
158
- def getSendPacket(self, message, msgtype):
163
+ def get_send_packet(self, message, msgtype):
159
164
  magic = struct.pack(">H", 0x1EED)
160
165
  _msgtype = struct.pack(">h", msgtype)
161
166
 
@@ -169,7 +174,7 @@ class DeviceClient(object):
169
174
 
170
175
  return packet
171
176
 
172
- async def login(self):
177
+ async def login(self) -> None:
173
178
  login_seq = str(int(time.time() * 1000) + self._login_uuid)[-9:]
174
179
  self._login_uuid += 1
175
180
  timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
@@ -187,7 +192,7 @@ class DeviceClient(object):
187
192
  },
188
193
  }
189
194
  try:
190
- self.writer.write(self.getSendPacket(json.dumps(message).encode(), 1))
195
+ self.writer.write(self.get_send_packet(json.dumps(message).encode(), 1))
191
196
  await self.writer.drain()
192
197
  data = await self.reader.read(1024)
193
198
  except (BrokenPipeError, ConnectionResetError) as e:
@@ -211,47 +216,87 @@ class DeviceClient(object):
211
216
  self.ascNumber = json_data[CONF_PAYLOAD][CONF_ASCNUMBER]
212
217
  self.ascNumber += 1
213
218
  self.status.online = True
219
+ asyncio.get_running_loop().create_task(self.reveive_data())
214
220
  await self.send_action({}, "getDevAttrReq")
215
221
 
216
- async def read_status(self):
217
- if self._connect_and_login is False:
218
- await asyncio.sleep(2)
219
- raise AidotNotLogin
220
- try:
221
- data = await self.reader.read(1024)
222
- except (BrokenPipeError, ConnectionResetError) as e:
223
- _LOGGER.error(f"{self.device_id} read status error {e}")
224
- await self.reset()
225
- self.status.online = False
226
- return self.status
227
- except Exception as e:
228
- _LOGGER.error(f"recv data error {e}")
229
- return self.status
230
- data_len = len(data)
231
- if data_len <= 0:
232
- _LOGGER.error("recv data error len")
233
- await self.reset()
234
- self.status.online = False
235
- return self.status
236
- try:
237
- magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
238
- decrypted_data = aes_decrypt(data[8:], self.aes_key)
239
- json_data = json.loads(decrypted_data)
240
- except Exception as e:
241
- _LOGGER.error(f"recv json error : {e}")
242
- return await self.read_status()
243
-
244
- if "service" in json_data:
245
- if "test" == json_data["service"]:
246
- self.ping_count = 0
247
- return await self.read_status()
248
- payload = json_data.get(CONF_PAYLOAD)
249
- if payload is not None:
250
- self.ascNumber = payload.get(CONF_ASCNUMBER)
251
- self.status.update(payload.get(CONF_ATTR))
222
+ async def reveive_data(self) -> None:
223
+ while True:
224
+ try:
225
+ data = await self.reader.read(1024)
226
+ except (BrokenPipeError, ConnectionResetError) as e:
227
+ _LOGGER.error(f"{self.device_id} read status error {e}")
228
+ await self.reset()
229
+ self.status.online = False
230
+ return
231
+ except Exception as e:
232
+ _LOGGER.error(f"recv data error {e}")
233
+ return
234
+ data_len = len(data)
235
+ if data_len <= 0:
236
+ _LOGGER.error("recv data error len, exit socket")
237
+ await self.reset()
238
+ self.status.online = False
239
+ return
240
+ try:
241
+ magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
242
+ decrypted_data = aes_decrypt(data[8:], self.aes_key)
243
+ json_data = json.loads(decrypted_data)
244
+ except Exception as e:
245
+ _LOGGER.error(f"recv json error : {e}")
246
+
247
+ if "service" in json_data:
248
+ if "test" == json_data["service"]:
249
+ self.ping_count = 0
250
+ continue
251
+
252
+ payload = json_data.get(CONF_PAYLOAD)
253
+ if payload is not None:
254
+ self.ascNumber = payload.get(CONF_ASCNUMBER)
255
+ self.status.update(payload.get(CONF_ATTR))
256
+ # _LOGGER.info(f"recv status : {payload}")
257
+ if self._status_fresh_cb:
258
+ self._status_fresh_cb(self.status)
259
+ def set_status_fresh_cb(self, callback) -> None:
260
+ self._status_fresh_cb = callback
261
+ async def read_status(self) -> DeviceStatusData:
262
+ # if self._connect_and_login is False:
263
+ # await asyncio.sleep(2)
264
+ # raise AidotNotLogin
265
+ # try:
266
+ # data = await self.reader.read(1024)
267
+ # except (BrokenPipeError, ConnectionResetError) as e:
268
+ # _LOGGER.error(f"{self.device_id} read status error {e}")
269
+ # await self.reset()
270
+ # self.status.online = False
271
+ # return self.status
272
+ # except Exception as e:
273
+ # _LOGGER.error(f"recv data error {e}")
274
+ # return self.status
275
+ # data_len = len(data)
276
+ # if data_len <= 0:
277
+ # _LOGGER.error("recv data error len")
278
+ # await self.reset()
279
+ # self.status.online = False
280
+ # return self.status
281
+ # try:
282
+ # magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
283
+ # decrypted_data = aes_decrypt(data[8:], self.aes_key)
284
+ # json_data = json.loads(decrypted_data)
285
+ # except Exception as e:
286
+ # _LOGGER.error(f"recv json error : {e}")
287
+ # return await self.read_status()
288
+
289
+ # if "service" in json_data:
290
+ # if "test" == json_data["service"]:
291
+ # self.ping_count = 0
292
+ # return await self.read_status()
293
+ # payload = json_data.get(CONF_PAYLOAD)
294
+ # if payload is not None:
295
+ # self.ascNumber = payload.get(CONF_ASCNUMBER)
296
+ # self.status.update(payload.get(CONF_ATTR))
252
297
  return self.status
253
298
 
254
- async def ping_task(self):
299
+ async def ping_task(self) -> None:
255
300
  while True:
256
301
  if self._is_close:
257
302
  return
@@ -259,7 +304,9 @@ class DeviceClient(object):
259
304
  await self.send_ping_action()
260
305
  await asyncio.sleep(5)
261
306
 
262
- async def send_dev_attr(self, dev_attr):
307
+ async def send_dev_attr(self, dev_attr) -> None:
308
+ if not self._connect_and_login:
309
+ raise ConnectionError('Device offline')
263
310
  await self.send_action(dev_attr, "setDevAttrReq")
264
311
 
265
312
  async def async_turn_off(self) -> None:
@@ -279,7 +326,7 @@ class DeviceClient(object):
279
326
  async def async_set_cct(self, cct: int) -> None:
280
327
  await self.send_dev_attr({CONF_CCT: cct})
281
328
 
282
- async def send_action(self, attr, method):
329
+ async def send_action(self, attr, method) -> None:
283
330
  current_timestamp_milliseconds = int(time.time() * 1000)
284
331
  self.seq_num += 1
285
332
  seq = "ha93" + str(self.seq_num).zfill(5)
@@ -321,7 +368,7 @@ class DeviceClient(object):
321
368
  }
322
369
 
323
370
  try:
324
- self.writer.write(self.getSendPacket(json.dumps(action).encode(), 1))
371
+ self.writer.write(self.get_send_packet(json.dumps(action).encode(), 1))
325
372
  await self.writer.drain()
326
373
  except (BrokenPipeError, ConnectionResetError) as e:
327
374
  _LOGGER.error(f"{self.device_id} send action error {e}")
@@ -329,7 +376,7 @@ class DeviceClient(object):
329
376
  except Exception as e:
330
377
  _LOGGER.error(f"{self.device_id} send action error {e}")
331
378
 
332
- async def send_ping_action(self):
379
+ async def send_ping_action(self) -> int:
333
380
  ping = {
334
381
  "service": "test",
335
382
  "method": "pingreq",
@@ -346,7 +393,7 @@ class DeviceClient(object):
346
393
  return -1
347
394
  if self._connect_and_login is False:
348
395
  return -1
349
- self.writer.write(self.getSendPacket(json.dumps(ping).encode(), 2))
396
+ self.writer.write(self.get_send_packet(json.dumps(ping).encode(), 2))
350
397
  await self.writer.drain()
351
398
  self.ping_count += 1
352
399
  return 1
@@ -355,7 +402,7 @@ class DeviceClient(object):
355
402
  await self.reset()
356
403
  return -1
357
404
 
358
- async def reset(self):
405
+ async def reset(self) -> None:
359
406
  try:
360
407
  if self.writer:
361
408
  self.writer.close()
@@ -366,7 +413,7 @@ class DeviceClient(object):
366
413
  self.status.online = False
367
414
  self.ping_count = 0
368
415
 
369
- async def close(self):
416
+ async def close(self) -> None:
370
417
  self._is_close = True
371
418
  await self.reset()
372
419
  _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:Any] = None
85
+ _login_info: dict[str, Any] = None
85
86
  _broadcast_protocol: BroadcastProtocol = None
86
- discovered_device: dict[str: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: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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: python-aidot
3
- Version: 0.3.4
3
+ Version: 0.3.42
4
4
  Summary: aidot control wifi lights
5
5
  Home-page: https://github.com/Aidot-Development-Team/python-aidot
6
6
  Author: aidotdev2024
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="python-aidot",
8
- version="0.3.4",
8
+ version="0.3.42",
9
9
  author="aidotdev2024",
10
10
  url='https://github.com/Aidot-Development-Team/python-aidot',
11
11
  description="aidot control wifi lights",
File without changes
File without changes
File without changes