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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: python-aidot
3
- Version: 0.3.41
3
+ Version: 0.3.43
4
4
  Summary: aidot control wifi lights
5
5
  Home-page: https://github.com/Aidot-Development-Team/python-aidot
6
6
  Author: aidotdev2024
@@ -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
- country_name: str | None = None,
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["name"] == self.country_name:
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:UnitedStates",
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/ME", "name": "Serbia", "ext": "", "region": "EU"},
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
- magic, msgtype, bodysize = struct.unpack(">HHI", data[:8])
203
- encrypted_data = data[8:]
204
- if self.aes_key is not None:
205
- decrypted_data = aes_decrypt(encrypted_data, self.aes_key)
206
- else:
207
- decrypted_data = encrypted_data
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
- json_data = json.loads(decrypted_data)
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
- self.ascNumber = json_data[CONF_PAYLOAD][CONF_ASCNUMBER]
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
- 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))
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: python-aidot
3
- Version: 0.3.41
3
+ Version: 0.3.43
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.41",
8
+ version="0.3.43",
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