aioamazondevices 3.0.1__tar.gz → 3.0.3__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.3
2
2
  Name: aioamazondevices
3
- Version: 3.0.1
3
+ Version: 3.0.3
4
4
  Summary: Python library to control Amazon devices
5
5
  License: Apache-2.0
6
6
  Author: Simone Chemelli
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aioamazondevices"
3
- version = "3.0.1"
3
+ version = "3.0.3"
4
4
  description = "Python library to control Amazon devices"
5
5
  authors = ["Simone Chemelli <simone.chemelli@gmail.com>"]
6
6
  license = "Apache-2.0"
@@ -1,6 +1,6 @@
1
1
  """aioamazondevices library."""
2
2
 
3
- __version__ = "3.0.1"
3
+ __version__ = "3.0.3"
4
4
 
5
5
 
6
6
  from .api import AmazonDevice, AmazonEchoApi
@@ -18,7 +18,9 @@ import orjson
18
18
  from aiohttp import ClientResponse, ClientSession
19
19
  from babel import Locale
20
20
  from bs4 import BeautifulSoup, Tag
21
- from yarl import URL
21
+ from httpx import URL as HTTPX_URL
22
+ from multidict import MultiDictProxy
23
+ from yarl import URL as YARL_URL
22
24
 
23
25
  from .const import (
24
26
  _LOGGER,
@@ -116,13 +118,12 @@ class AmazonEchoApi:
116
118
  self._domain = domain
117
119
  self._cookies = self._build_init_cookies()
118
120
  self._csrf_cookie: str | None = None
119
- self._headers = DEFAULT_HEADERS
120
121
  self._save_raw_data = save_raw_data
121
122
  self._login_stored_data = login_data
122
123
  self._serial = self._serial_number()
123
124
  self._list_for_clusters: dict[str, str] = {}
124
125
 
125
- self.session: ClientSession
126
+ self.session: ClientSession | HttpxClientSession
126
127
 
127
128
  def _load_website_cookies(self) -> dict[str, str]:
128
129
  """Get website cookies, if avaliables."""
@@ -162,7 +163,7 @@ class AmazonEchoApi:
162
163
  map_md_str = orjson.dumps(map_md_dict).decode("utf-8")
163
164
  map_md = base64.b64encode(map_md_str.encode()).decode().rstrip("=")
164
165
 
165
- return {"frc": frc, "map-md": map_md, "amzn-app-id": AMAZON_APP_ID}
166
+ return {"amzn-app-id": AMAZON_APP_ID, "frc": frc, "map-md": map_md}
166
167
 
167
168
  def _create_code_verifier(self, length: int = 32) -> bytes:
168
169
  """Create code verifier."""
@@ -213,11 +214,11 @@ class AmazonEchoApi:
213
214
  form = soup.find("form", {"name": "signIn"}) or soup.find("form")
214
215
 
215
216
  if not isinstance(form, Tag):
216
- raise TypeError("No form found in page or something other is going wrong.")
217
+ raise TypeError("Unable to find form in login response")
217
218
 
218
219
  inputs = {}
219
220
  for field in form.find_all("input"):
220
- if field.get("type") and field["type"] == "hidden":
221
+ if isinstance(field, Tag) and field.get("type", "") == "hidden":
221
222
  inputs[field["name"]] = field.get("value", "")
222
223
 
223
224
  return inputs
@@ -227,15 +228,22 @@ class AmazonEchoApi:
227
228
  _LOGGER.debug("Get request data from HTML source")
228
229
  form = soup.find("form", {"name": "signIn"}) or soup.find("form")
229
230
  if isinstance(form, Tag):
230
- method = form["method"]
231
- url = form["action"]
231
+ method = form.get("method")
232
+ url = form.get("action")
232
233
  if isinstance(method, str) and isinstance(url, str):
233
234
  return method, url
234
- raise TypeError("Unable to extract form data from response.")
235
+ raise TypeError("Unable to extract form data from response")
235
236
 
236
- def _extract_code_from_url(self, url: URL) -> str:
237
+ def _extract_code_from_url(self, url: YARL_URL | HTTPX_URL) -> str:
237
238
  """Extract the access token from url query after login."""
238
- parsed_url = parse_qs(url.query.decode())
239
+ parsed_url: dict[str, list[str]] = {}
240
+ if isinstance(url.query, bytes):
241
+ parsed_url = parse_qs(url.query.decode())
242
+ elif isinstance(url.query, MultiDictProxy):
243
+ for key, value in url.query.items():
244
+ parsed_url[key] = [value]
245
+ else:
246
+ raise TypeError(f"Unable to extract authorization code from url: {url}")
239
247
  return parsed_url["openid.oa2.authorization_code"][0]
240
248
 
241
249
  def _client_session(self) -> None:
@@ -281,9 +289,13 @@ class AmazonEchoApi:
281
289
  _LOGGER.debug("Adding %s to headers", json_header)
282
290
  headers.update(json_header)
283
291
 
292
+ _url: YARL_URL | str = url
293
+ if LIBRARY == "aiohttp":
294
+ _url = YARL_URL(url, encoded=True)
295
+
284
296
  resp = await self.session.request(
285
297
  method,
286
- url,
298
+ _url,
287
299
  data=input_data if not json_data else orjson.dumps(input_data),
288
300
  cookies=self._load_website_cookies(),
289
301
  headers=headers,
@@ -325,7 +337,8 @@ class AmazonEchoApi:
325
337
  base_filename = url
326
338
  fullpath = Path(output_dir, base_filename + extension)
327
339
 
328
- if type(raw_data) is dict:
340
+ data: str
341
+ if isinstance(raw_data, dict):
329
342
  data = orjson.dumps(raw_data, option=orjson.OPT_INDENT_2).decode("utf-8")
330
343
  elif extension in [HTML_EXTENSION, BIN_EXTENSION]:
331
344
  data = raw_data
@@ -484,22 +497,13 @@ class AmazonEchoApi:
484
497
  url=login_url,
485
498
  input_data=login_inputs,
486
499
  )
500
+ _LOGGER.debug("Login response url:%s", login_resp.url)
487
501
 
488
- authcode_url = None
489
- _LOGGER.debug("Login query: %s", login_resp.url.query)
490
- if b"openid.oa2.authorization_code" in login_resp.url.query:
491
- authcode_url = login_resp.url
492
- elif len(login_resp.history) > 0:
493
- for history in login_resp.history:
494
- if b"openid.oa2.authorization_code" in history.url.query:
495
- authcode_url = history.url
496
- break
497
-
498
- if authcode_url is None:
499
- raise CannotAuthenticate
502
+ authcode = self._extract_code_from_url(login_resp.url)
503
+ _LOGGER.debug("Login extracted authcode: %s", authcode)
500
504
 
501
505
  device_login_data = {
502
- "authorization_code": self._extract_code_from_url(authcode_url),
506
+ "authorization_code": authcode,
503
507
  "code_verifier": code_verifier,
504
508
  "domain": self._domain,
505
509
  }
@@ -42,6 +42,7 @@ DEFAULT_HEADERS = {
42
42
  ),
43
43
  "Accept-Language": "en-US",
44
44
  "Accept-Encoding": "gzip",
45
+ "Connection": "keep-alive",
45
46
  }
46
47
  CSRF_COOKIE = "csrf"
47
48
 
@@ -66,10 +67,26 @@ BIN_EXTENSION = ".bin"
66
67
  SPEAKER_GROUP_MODEL = "Speaker Group"
67
68
 
68
69
  DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
70
+ "A10A33FOX2NUBK": {
71
+ "model": "Echo Spot",
72
+ "hw_version": "Gen1",
73
+ },
74
+ "A11QM4H9HGV71H": {
75
+ "model": "Echo Show 5",
76
+ "hw_version": "Gen3",
77
+ },
78
+ "A1Q6UGEXJZWJQ0": {
79
+ "model": "Fire TV Stick 4K",
80
+ "hw_version": "Gen2",
81
+ },
69
82
  "A1RABVCI4QCIKC": {
70
83
  "model": "Echo Dot",
71
84
  "hw_version": "Gen3",
72
85
  },
86
+ "A271DR1789MXDS": {
87
+ "model": "Fire Tablet 7",
88
+ "hw_version": "Gen12",
89
+ },
73
90
  "A2DS1Q2TPDJ48U": {
74
91
  "model": "Echo Dot Clock",
75
92
  "hw_version": "Gen5",
@@ -78,10 +95,26 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
78
95
  "model": "Echo Dot Clock",
79
96
  "hw_version": "Gen4",
80
97
  },
98
+ "A2LWARUGJLBYEW": {
99
+ "model": "Fire TV Stick",
100
+ "hw_version": "Gen2",
101
+ },
102
+ "A2M4YX06LWP8WI": {
103
+ "model": "Fire Tablet 7",
104
+ "hw_version": "Gen5",
105
+ },
81
106
  "A2U21SRK4QGSE1": {
82
107
  "model": "Echo Dot Clock",
83
108
  "hw_version": "Gen4",
84
109
  },
110
+ "A303PJF6ISQ7IC": {
111
+ "model": "Echo Auto",
112
+ "hw_version": "Gen1",
113
+ },
114
+ "A30YDR2MK8HMRV": {
115
+ "model": "Echo Dot",
116
+ "hw_version": "Gen3",
117
+ },
85
118
  "A32DDESGESSHZA": {
86
119
  "model": "Echo Dot",
87
120
  "hw_version": "Gen3",
@@ -90,6 +123,10 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
90
123
  "model": "Echo Dot",
91
124
  "hw_version": "Gen3",
92
125
  },
126
+ "A3C9PE6TNYLTCH": {
127
+ "model": "Speaker Group",
128
+ "hw_version": "None",
129
+ },
93
130
  "A3RMGO6LYLH7YN": {
94
131
  "model": "Echo Dot",
95
132
  "hw_version": "Gen4",
@@ -98,28 +135,44 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
98
135
  "model": "Echo Dot",
99
136
  "hw_version": "Gen2",
100
137
  },
138
+ "A3VRME03NAXFUB": {
139
+ "model": "Echo Flex",
140
+ "hw_version": "None",
141
+ },
142
+ "A4ZP7ZC4PI6TO": {
143
+ "model": "Echo Show 3",
144
+ "hw_version": "Gen1",
145
+ },
101
146
  "A4ZXE0RM7LQ7A": {
102
147
  "model": "Echo Dot",
103
148
  "hw_version": "Gen5",
104
149
  },
150
+ "A7WXQPH584YP": {
151
+ "model": "Echo Dot",
152
+ "hw_version": "Gen2",
153
+ },
154
+ "AB72C64C86AW2": {
155
+ "model": "Echo Dot",
156
+ "hw_version": "Gen2",
157
+ },
105
158
  "AKNO1N0KSFN8L": {
106
159
  "model": "Echo Dot",
107
160
  "hw_version": "Gen1",
108
161
  },
109
- "A3C9PE6TNYLTCH": {
110
- "model": SPEAKER_GROUP_MODEL,
111
- "hw_version": None,
162
+ "AKPGW064GI9HE": {
163
+ "model": " Fire TV Stick 4K",
164
+ "hw_version": "Gen1",
112
165
  },
113
- "A1Q6UGEXJZWJQ0": {
114
- "model": "Fire TV Stick",
115
- "hw_version": "4K",
166
+ "ATNLRCEBX3W4P": {
167
+ "model": "Fire Tablet HD 10",
168
+ "hw_version": "Gen11",
116
169
  },
117
- "A10A33FOX2NUBK": {
118
- "model": "Echo Spot",
119
- "hw_version": "Gen1",
170
+ "AVU7CPPF2ZRAS": {
171
+ "model": "Fire Tablet HD 8 Plus",
172
+ "hw_version": "Gen10",
120
173
  },
121
- "AB72C64C86AW2": {
122
- "model": "Echo Dot",
174
+ "G2A0V704840708AP": {
175
+ "model": "Echo Plus",
123
176
  "hw_version": "Gen2",
124
177
  },
125
178
  }