python-aidot 0.2.5__tar.gz → 0.2.6__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.2.5
3
+ Version: 0.2.6
4
4
  Summary: aidot control wifi lights
5
5
  Home-page: https://github.com/Aidot-Development-Team/python-aidot
6
6
  Author: aidotdev2024
@@ -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.username}-{self.password}-{self._region}"
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
- async with self.session.post(url, headers=headers, json=data) as response:
89
- response.raise_for_status()
90
- self.login_info = await response.json()
91
- return self.login_info
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
- async with self.session.post(url, headers=headers, json=data) as response:
105
- response.raise_for_status()
106
- data = await response.json()
107
- self.login_info[CONF_ACCESS_TOKEN] = data[CONF_ACCESS_TOKEN]
108
- return data
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("async_post_login ClientError {e}")
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
- "Token": token,
153
+ CONF_TOKEN: token,
122
154
  CONF_APP_ID: APP_ID,
123
155
  }
124
156
  try:
125
- async with self.session.get(url, headers=headers) as response:
126
- response.raise_for_status()
127
- response_data = await response.json()
128
- return response_data
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("async_get_products ClientError {e}")
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, token: str):
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
- for house in houses:
153
- # get device_list
154
- device_list = await self.async_get_devices(house[CONF_ID])
155
- if device_list:
156
- final_device_list.extend(device_list)
157
-
158
- # get product_list
159
- productIds = ",".join([item[CONF_PRODUCT_ID] for item in device_list])
160
- product_list = await self.async_get_products(productIds)
161
-
162
- for product in product_list:
163
- for device in final_device_list:
164
- if device[CONF_PRODUCT_ID] == product[CONF_ID]:
165
- device[CONF_PRODUCT] = product
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 AidotAuthVerificationCode(AidotError):
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
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: python-aidot
3
- Version: 0.2.5
3
+ Version: 0.2.6
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.2.5",
8
+ version="0.2.6",
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
File without changes