aioamazondevices 4.0.2__tar.gz → 5.0.1__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.
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/PKG-INFO +2 -2
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/README.md +0 -1
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/pyproject.toml +2 -1
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/__init__.py +1 -1
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/api.py +182 -84
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/const.py +14 -52
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/exceptions.py +0 -4
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/LICENSE +0 -0
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/py.typed +0 -0
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/sounds.py +0 -0
- {aioamazondevices-4.0.2 → aioamazondevices-5.0.1}/src/aioamazondevices/utils.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: aioamazondevices
|
3
|
-
Version:
|
3
|
+
Version: 5.0.1
|
4
4
|
Summary: Python library to control Amazon devices
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: Simone Chemelli
|
@@ -19,6 +19,7 @@ Requires-Dist: orjson
|
|
19
19
|
Requires-Dist: yarl
|
20
20
|
Project-URL: Bug Tracker, https://github.com/chemelli74/aioamazondevices/issues
|
21
21
|
Project-URL: Changelog, https://github.com/chemelli74/aioamazondevices/blob/main/CHANGELOG.md
|
22
|
+
Project-URL: Homepage, https://github.com/chemelli74/aioamazondevices
|
22
23
|
Description-Content-Type: text/markdown
|
23
24
|
|
24
25
|
# aioamazondevices
|
@@ -74,7 +75,6 @@ The script accept command line arguments or a library_test.json config file:
|
|
74
75
|
|
75
76
|
```json
|
76
77
|
{
|
77
|
-
"country": "IT",
|
78
78
|
"email": "<my_address@gmail.com>",
|
79
79
|
"password": "<my_password>",
|
80
80
|
"single_device_name": "Echo Dot Livingroom",
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[project]
|
2
2
|
name = "aioamazondevices"
|
3
|
-
version = "
|
3
|
+
version = "5.0.1"
|
4
4
|
requires-python = ">=3.12"
|
5
5
|
description = "Python library to control Amazon devices"
|
6
6
|
authors = [
|
@@ -29,6 +29,7 @@ dependencies = [
|
|
29
29
|
]
|
30
30
|
|
31
31
|
[project.urls]
|
32
|
+
"Homepage" = "https://github.com/chemelli74/aioamazondevices"
|
32
33
|
"Bug Tracker" = "https://github.com/chemelli74/aioamazondevices/issues"
|
33
34
|
"Changelog" = "https://github.com/chemelli74/aioamazondevices/blob/main/CHANGELOG.md"
|
34
35
|
|
@@ -16,7 +16,11 @@ from typing import Any, cast
|
|
16
16
|
from urllib.parse import parse_qs, urlencode
|
17
17
|
|
18
18
|
import orjson
|
19
|
-
from aiohttp import
|
19
|
+
from aiohttp import (
|
20
|
+
ClientConnectorError,
|
21
|
+
ClientResponse,
|
22
|
+
ClientSession,
|
23
|
+
)
|
20
24
|
from bs4 import BeautifulSoup, Tag
|
21
25
|
from langcodes import Language
|
22
26
|
from multidict import MultiDictProxy
|
@@ -37,9 +41,9 @@ from .const import (
|
|
37
41
|
DEFAULT_AGENT,
|
38
42
|
DEFAULT_ASSOC_HANDLE,
|
39
43
|
DEFAULT_HEADERS,
|
44
|
+
DEFAULT_SITE,
|
40
45
|
DEVICE_TO_IGNORE,
|
41
46
|
DEVICE_TYPE_TO_MODEL,
|
42
|
-
DOMAIN_BY_ISO3166_COUNTRY,
|
43
47
|
HTML_EXTENSION,
|
44
48
|
HTTP_ERROR_199,
|
45
49
|
HTTP_ERROR_299,
|
@@ -49,6 +53,8 @@ from .const import (
|
|
49
53
|
NODE_DO_NOT_DISTURB,
|
50
54
|
NODE_IDENTIFIER,
|
51
55
|
NODE_PREFERENCES,
|
56
|
+
REFRESH_ACCESS_TOKEN,
|
57
|
+
REFRESH_AUTH_COOKIES,
|
52
58
|
SAVE_PATH,
|
53
59
|
SENSORS,
|
54
60
|
URI_IDS,
|
@@ -61,7 +67,6 @@ from .exceptions import (
|
|
61
67
|
CannotConnect,
|
62
68
|
CannotRegisterDevice,
|
63
69
|
CannotRetrieveData,
|
64
|
-
WrongCountry,
|
65
70
|
WrongMethod,
|
66
71
|
)
|
67
72
|
from .utils import obfuscate_email, scrub_fields
|
@@ -86,7 +91,6 @@ class AmazonDevice:
|
|
86
91
|
device_type: str
|
87
92
|
device_owner_customer_id: str
|
88
93
|
device_cluster_members: list[str]
|
89
|
-
device_locale: str
|
90
94
|
online: bool
|
91
95
|
serial_number: str
|
92
96
|
software_version: str
|
@@ -122,32 +126,22 @@ class AmazonEchoApi:
|
|
122
126
|
def __init__(
|
123
127
|
self,
|
124
128
|
client_session: ClientSession,
|
125
|
-
login_country_code: str,
|
126
129
|
login_email: str,
|
127
130
|
login_password: str,
|
128
131
|
login_data: dict[str, Any] | None = None,
|
129
132
|
) -> None:
|
130
133
|
"""Initialize the scanner."""
|
131
|
-
#
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
domain: str = locale.get("domain", country_code)
|
136
|
-
market: list[str] = locale.get("market", [f"https://www.amazon.{domain}"])
|
137
|
-
assoc_handle: str = locale.get(
|
138
|
-
"openid.assoc_handle", f"{DEFAULT_ASSOC_HANDLE}_{country_code}"
|
139
|
-
)
|
134
|
+
# Check if there is a previous login, otherwise use default (US)
|
135
|
+
site = login_data.get("site", DEFAULT_SITE) if login_data else DEFAULT_SITE
|
136
|
+
_LOGGER.debug("Using site: %s", site)
|
137
|
+
self._country_specific_data(site)
|
140
138
|
|
141
|
-
self._assoc_handle = assoc_handle
|
142
139
|
self._login_email = login_email
|
143
140
|
self._login_password = login_password
|
144
|
-
|
145
|
-
self._domain = domain
|
146
|
-
self._market = market
|
141
|
+
|
147
142
|
self._cookies = self._build_init_cookies()
|
148
|
-
self._csrf_cookie: str | None = None
|
149
143
|
self._save_raw_data = False
|
150
|
-
self._login_stored_data = login_data
|
144
|
+
self._login_stored_data = login_data or {}
|
151
145
|
self._serial = self._serial_number()
|
152
146
|
self._list_for_clusters: dict[str, str] = {}
|
153
147
|
|
@@ -155,23 +149,40 @@ class AmazonEchoApi:
|
|
155
149
|
self._devices: dict[str, Any] = {}
|
156
150
|
self._sensors_available: bool = True
|
157
151
|
|
158
|
-
|
152
|
+
_LOGGER.debug("Initialize library v%s", __version__)
|
153
|
+
|
154
|
+
@property
|
155
|
+
def domain(self) -> str:
|
156
|
+
"""Return current Amazon domain."""
|
157
|
+
return self._domain
|
158
|
+
|
159
|
+
def save_raw_data(self) -> None:
|
160
|
+
"""Save raw data to disk."""
|
161
|
+
self._save_raw_data = True
|
162
|
+
_LOGGER.debug("Saving raw data to disk")
|
163
|
+
|
164
|
+
def _country_specific_data(self, domain: str) -> None:
|
165
|
+
"""Set country specific data."""
|
166
|
+
# Force lower case
|
167
|
+
domain = domain.replace("https://www.amazon.", "").lower()
|
168
|
+
country_code = domain.split(".")[-1] if domain != "com" else "us"
|
169
|
+
|
170
|
+
lang_object = Language.make(territory=country_code.upper())
|
159
171
|
lang_maximized = lang_object.maximize()
|
172
|
+
|
173
|
+
self._domain: str = domain
|
160
174
|
self._language = f"{lang_maximized.language}-{lang_maximized.region}"
|
161
175
|
|
176
|
+
# Reset CSRF cookie when changing country
|
177
|
+
self._csrf_cookie: str | None = None
|
178
|
+
|
162
179
|
_LOGGER.debug(
|
163
|
-
"Initialize
|
164
|
-
|
180
|
+
"Initialize country <%s>: domain <amazon.%s>, language <%s>",
|
181
|
+
country_code.upper(),
|
165
182
|
self._domain,
|
166
183
|
self._language,
|
167
|
-
self._market,
|
168
184
|
)
|
169
185
|
|
170
|
-
def save_raw_data(self) -> None:
|
171
|
-
"""Save raw data to disk."""
|
172
|
-
self._save_raw_data = True
|
173
|
-
_LOGGER.debug("Saving raw data to disk")
|
174
|
-
|
175
186
|
def _load_website_cookies(self) -> dict[str, str]:
|
176
187
|
"""Get website cookies, if avaliables."""
|
177
188
|
if not self._login_stored_data:
|
@@ -246,11 +257,11 @@ class AmazonEchoApi:
|
|
246
257
|
code_challenge = self._create_s256_code_challenge(code_verifier)
|
247
258
|
|
248
259
|
oauth_params = {
|
249
|
-
"openid.return_to":
|
260
|
+
"openid.return_to": "https://www.amazon.com/ap/maplanding",
|
250
261
|
"openid.oa2.code_challenge_method": "S256",
|
251
|
-
"openid.assoc_handle":
|
262
|
+
"openid.assoc_handle": DEFAULT_ASSOC_HANDLE,
|
252
263
|
"openid.identity": "http://specs.openid.net/auth/2.0/identifier_select",
|
253
|
-
"pageId":
|
264
|
+
"pageId": DEFAULT_ASSOC_HANDLE,
|
254
265
|
"accountStatusPolicy": "P1",
|
255
266
|
"openid.claimed_id": "http://specs.openid.net/auth/2.0/identifier_select",
|
256
267
|
"openid.mode": "checkid_setup",
|
@@ -265,9 +276,7 @@ class AmazonEchoApi:
|
|
265
276
|
"openid.oa2.response_type": "code",
|
266
277
|
}
|
267
278
|
|
268
|
-
return (
|
269
|
-
f"https://www.amazon.{self._domain}{URI_SIGNIN}?{urlencode(oauth_params)}"
|
270
|
-
)
|
279
|
+
return f"https://www.amazon.com{URI_SIGNIN}?{urlencode(oauth_params)}"
|
271
280
|
|
272
281
|
def _get_inputs_from_soup(self, soup: BeautifulSoup) -> dict[str, str]:
|
273
282
|
"""Extract hidden form input fields from a Amazon login page."""
|
@@ -352,7 +361,7 @@ class AmazonEchoApi:
|
|
352
361
|
json_data,
|
353
362
|
)
|
354
363
|
|
355
|
-
headers = DEFAULT_HEADERS
|
364
|
+
headers = DEFAULT_HEADERS.copy()
|
356
365
|
headers.update({"Accept-Language": self._language})
|
357
366
|
if not amazon_user_agent:
|
358
367
|
_LOGGER.debug("Changing User-Agent to %s", DEFAULT_AGENT)
|
@@ -370,7 +379,15 @@ class AmazonEchoApi:
|
|
370
379
|
_cookies = (
|
371
380
|
self._load_website_cookies() if self._login_stored_data else self._cookies
|
372
381
|
)
|
373
|
-
self._session.cookie_jar.update_cookies(_cookies)
|
382
|
+
self._session.cookie_jar.update_cookies(_cookies, URL(f"amazon.{self._domain}"))
|
383
|
+
|
384
|
+
if url.endswith("/auth/token"):
|
385
|
+
headers.update(
|
386
|
+
{
|
387
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
388
|
+
"x-amzn-identity-auth-domain": "api.amazon.com",
|
389
|
+
}
|
390
|
+
)
|
374
391
|
|
375
392
|
resp: ClientResponse | None = None
|
376
393
|
for delay in [0, 1, 2, 5, 8, 12, 21]:
|
@@ -405,15 +422,18 @@ class AmazonEchoApi:
|
|
405
422
|
_LOGGER.error("No response received from %s", url)
|
406
423
|
raise CannotConnect(f"No response received from {url}")
|
407
424
|
|
408
|
-
if not self._csrf_cookie
|
409
|
-
|
410
|
-
|
425
|
+
if not self._csrf_cookie and (
|
426
|
+
csrf := resp.cookies.get(CSRF_COOKIE, Morsel()).value
|
427
|
+
):
|
428
|
+
self._csrf_cookie = csrf
|
429
|
+
_LOGGER.debug("CSRF cookie value: <%s> [%s]", self._csrf_cookie, url)
|
411
430
|
|
412
431
|
content_type: str = resp.headers.get("Content-Type", "")
|
413
432
|
_LOGGER.debug(
|
414
|
-
"Response
|
415
|
-
|
433
|
+
"Response for url %s :\nstatus : %s \
|
434
|
+
\ncontent type: %s ",
|
416
435
|
url,
|
436
|
+
resp.status,
|
417
437
|
content_type,
|
418
438
|
)
|
419
439
|
|
@@ -508,6 +528,7 @@ class AmazonEchoApi:
|
|
508
528
|
"software_version": AMAZON_DEVICE_SOFTWARE_VERSION,
|
509
529
|
},
|
510
530
|
"auth_data": {
|
531
|
+
"use_global_authentication": "true",
|
511
532
|
"client_id": self._build_client_id(),
|
512
533
|
"authorization_code": authorization_code,
|
513
534
|
"code_verifier": code_verifier.decode(),
|
@@ -523,7 +544,7 @@ class AmazonEchoApi:
|
|
523
544
|
],
|
524
545
|
}
|
525
546
|
|
526
|
-
register_url =
|
547
|
+
register_url = "https://api.amazon.com/auth/register"
|
527
548
|
_, resp = await self._session_request(
|
528
549
|
method=HTTPMethod.POST,
|
529
550
|
url=register_url,
|
@@ -573,31 +594,9 @@ class AmazonEchoApi:
|
|
573
594
|
"device_info": device_info,
|
574
595
|
"customer_info": customer_info,
|
575
596
|
}
|
576
|
-
|
597
|
+
_LOGGER.info("Register device: %s", scrub_fields(login_data))
|
577
598
|
return login_data
|
578
599
|
|
579
|
-
async def _check_country(self) -> None:
|
580
|
-
"""Check if user selected country matches Amazon account country."""
|
581
|
-
url = f"https://alexa.amazon.{self._domain}/api/users/me"
|
582
|
-
_, resp_me = await self._session_request(HTTPMethod.GET, url)
|
583
|
-
|
584
|
-
if resp_me.status != HTTPStatus.OK:
|
585
|
-
raise CannotAuthenticate
|
586
|
-
|
587
|
-
resp_me_json = await resp_me.json()
|
588
|
-
amazon_market = resp_me_json["marketPlaceDomainName"]
|
589
|
-
|
590
|
-
if amazon_market not in self._market:
|
591
|
-
_LOGGER.warning(
|
592
|
-
"Selected country <%s> doesn't match Amazon API reply:\n%s\n vs \n%s",
|
593
|
-
self._login_country_code.upper(),
|
594
|
-
{"input ": self._market},
|
595
|
-
{"amazon": amazon_market},
|
596
|
-
)
|
597
|
-
raise WrongCountry
|
598
|
-
|
599
|
-
_LOGGER.debug("User selected country matches Amazon API one")
|
600
|
-
|
601
600
|
async def _get_devices_ids(self) -> list[dict[str, str]]:
|
602
601
|
"""Retrieve devices entityId and applianceId."""
|
603
602
|
_, raw_resp = await self._session_request(
|
@@ -728,6 +727,22 @@ class AmazonEchoApi:
|
|
728
727
|
bool(otp_code),
|
729
728
|
)
|
730
729
|
|
730
|
+
device_login_data = await self._login_mode_interactive_oauth(otp_code)
|
731
|
+
|
732
|
+
login_data = await self._register_device(device_login_data)
|
733
|
+
self._login_stored_data = login_data
|
734
|
+
|
735
|
+
await self._domain_refresh_auth_cookies()
|
736
|
+
|
737
|
+
self._login_stored_data.update({"site": f"https://www.amazon.{self._domain}"})
|
738
|
+
await self._save_to_file(self._login_stored_data, "login_data", JSON_EXTENSION)
|
739
|
+
|
740
|
+
return self._login_stored_data
|
741
|
+
|
742
|
+
async def _login_mode_interactive_oauth(
|
743
|
+
self, otp_code: str
|
744
|
+
) -> dict[str, str | bytes]:
|
745
|
+
"""Login interactive via oauth URL."""
|
731
746
|
code_verifier = self._create_code_verifier()
|
732
747
|
client_id = self._build_client_id()
|
733
748
|
|
@@ -772,21 +787,12 @@ class AmazonEchoApi:
|
|
772
787
|
authcode = self._extract_code_from_url(login_resp.url)
|
773
788
|
_LOGGER.debug("Login extracted authcode: %s", authcode)
|
774
789
|
|
775
|
-
|
790
|
+
return {
|
776
791
|
"authorization_code": authcode,
|
777
792
|
"code_verifier": code_verifier,
|
778
793
|
"domain": self._domain,
|
779
794
|
}
|
780
795
|
|
781
|
-
register_device = await self._register_device(device_login_data)
|
782
|
-
self._login_stored_data = register_device
|
783
|
-
|
784
|
-
_LOGGER.info("Register device: %s", scrub_fields(register_device))
|
785
|
-
|
786
|
-
await self._check_country()
|
787
|
-
|
788
|
-
return register_device
|
789
|
-
|
790
796
|
async def login_mode_stored_data(self) -> dict[str, Any]:
|
791
797
|
"""Login to Amazon using previously stored data."""
|
792
798
|
if not self._login_stored_data:
|
@@ -801,10 +807,51 @@ class AmazonEchoApi:
|
|
801
807
|
obfuscate_email(self._login_email),
|
802
808
|
)
|
803
809
|
|
804
|
-
await self._check_country()
|
805
|
-
|
806
810
|
return self._login_stored_data
|
807
811
|
|
812
|
+
async def _get_alexa_domain(self) -> str:
|
813
|
+
"""Get the Alexa domain."""
|
814
|
+
_LOGGER.debug("Retrieve Alexa domain")
|
815
|
+
_, raw_resp = await self._session_request(
|
816
|
+
method=HTTPMethod.GET,
|
817
|
+
url=f"https://alexa.amazon.{self._domain}/api/welcome",
|
818
|
+
)
|
819
|
+
json_data = await raw_resp.json()
|
820
|
+
return cast(
|
821
|
+
"str", json_data.get("alexaHostName", f"alexa.amazon.{self._domain}")
|
822
|
+
)
|
823
|
+
|
824
|
+
async def _refresh_auth_cookies(self) -> None:
|
825
|
+
"""Refresh cookies after domain swap."""
|
826
|
+
_, json_token_resp = await self._refresh_data(REFRESH_AUTH_COOKIES)
|
827
|
+
|
828
|
+
# Need to take cookies from response and create them as cookies
|
829
|
+
website_cookies = self._login_stored_data["website_cookies"] = {}
|
830
|
+
self._session.cookie_jar.clear()
|
831
|
+
|
832
|
+
cookie_json = json_token_resp["response"]["tokens"]["cookies"]
|
833
|
+
for cookie_domain in cookie_json:
|
834
|
+
for cookie in cookie_json[cookie_domain]:
|
835
|
+
new_cookie_value = cookie["Value"].replace(r'"', r"")
|
836
|
+
new_cookie = {cookie["Name"]: new_cookie_value}
|
837
|
+
self._session.cookie_jar.update_cookies(new_cookie, URL(cookie_domain))
|
838
|
+
website_cookies.update(new_cookie)
|
839
|
+
if cookie["Name"] == "session-token":
|
840
|
+
self._login_stored_data["store_authentication_cookie"] = {
|
841
|
+
"cookie": new_cookie_value
|
842
|
+
}
|
843
|
+
|
844
|
+
async def _domain_refresh_auth_cookies(self) -> None:
|
845
|
+
"""Refresh cookies after domain swap."""
|
846
|
+
_LOGGER.debug("Refreshing auth cookies after domain change")
|
847
|
+
|
848
|
+
# Get the new Alexa domain
|
849
|
+
user_domain = (await self._get_alexa_domain()).replace("alexa", "https://www")
|
850
|
+
if user_domain != DEFAULT_SITE:
|
851
|
+
_LOGGER.debug("User domain changed to %s", user_domain)
|
852
|
+
self._country_specific_data(user_domain)
|
853
|
+
await self._refresh_auth_cookies()
|
854
|
+
|
808
855
|
async def get_devices_data(
|
809
856
|
self,
|
810
857
|
) -> dict[str, AmazonDevice]:
|
@@ -815,9 +862,6 @@ class AmazonEchoApi:
|
|
815
862
|
method=HTTPMethod.GET,
|
816
863
|
url=f"https://alexa.amazon.{self._domain}{URI_QUERIES[key]}",
|
817
864
|
)
|
818
|
-
_LOGGER.debug("Response URL: %s", raw_resp.url)
|
819
|
-
response_code = raw_resp.status
|
820
|
-
_LOGGER.debug("Response code: |%s|", response_code)
|
821
865
|
|
822
866
|
response_data = await raw_resp.text()
|
823
867
|
json_data = {} if len(response_data) == 0 else await raw_resp.json()
|
@@ -867,7 +911,6 @@ class AmazonEchoApi:
|
|
867
911
|
device_cluster_members=(
|
868
912
|
devices_node["clusterMembers"] or [serial_number]
|
869
913
|
),
|
870
|
-
device_locale=preferences_node.get("locale", self._language),
|
871
914
|
online=devices_node["online"],
|
872
915
|
serial_number=serial_number,
|
873
916
|
software_version=devices_node["softwareVersion"],
|
@@ -939,7 +982,7 @@ class AmazonEchoApi:
|
|
939
982
|
base_payload = {
|
940
983
|
"deviceType": device.device_type,
|
941
984
|
"deviceSerialNumber": device.serial_number,
|
942
|
-
"locale":
|
985
|
+
"locale": self._language,
|
943
986
|
"customerId": device.device_owner_customer_id,
|
944
987
|
}
|
945
988
|
|
@@ -974,7 +1017,7 @@ class AmazonEchoApi:
|
|
974
1017
|
"expireAfter": "PT5S",
|
975
1018
|
"content": [
|
976
1019
|
{
|
977
|
-
"locale":
|
1020
|
+
"locale": self._language,
|
978
1021
|
"display": {
|
979
1022
|
"title": "Home Assistant",
|
980
1023
|
"body": message_body,
|
@@ -1122,3 +1165,58 @@ class AmazonEchoApi:
|
|
1122
1165
|
await self._session_request(
|
1123
1166
|
method="PUT", url=url, input_data=payload, json_data=True
|
1124
1167
|
)
|
1168
|
+
|
1169
|
+
async def _refresh_data(self, data_type: str) -> tuple[bool, dict]:
|
1170
|
+
"""Refresh data."""
|
1171
|
+
if not self._login_stored_data:
|
1172
|
+
_LOGGER.debug("No login data available, cannot refresh")
|
1173
|
+
return False, {}
|
1174
|
+
|
1175
|
+
data = {
|
1176
|
+
"app_name": AMAZON_APP_NAME,
|
1177
|
+
"app_version": AMAZON_APP_VERSION,
|
1178
|
+
"di.sdk.version": "6.12.4",
|
1179
|
+
"source_token": self._login_stored_data["refresh_token"],
|
1180
|
+
"package_name": AMAZON_APP_BUNDLE_ID,
|
1181
|
+
"di.hw.version": "iPhone",
|
1182
|
+
"platform": "iOS",
|
1183
|
+
"requested_token_type": data_type,
|
1184
|
+
"source_token_type": "refresh_token",
|
1185
|
+
"di.os.name": "iOS",
|
1186
|
+
"di.os.version": AMAZON_CLIENT_OS,
|
1187
|
+
"current_version": "6.12.4",
|
1188
|
+
"previous_version": "6.12.4",
|
1189
|
+
"domain": f"www.amazon.{self._domain}",
|
1190
|
+
}
|
1191
|
+
|
1192
|
+
response = await self._session.post(
|
1193
|
+
"https://api.amazon.com/auth/token",
|
1194
|
+
data=data,
|
1195
|
+
)
|
1196
|
+
_LOGGER.debug(
|
1197
|
+
"Refresh data response %s with payload %s",
|
1198
|
+
response.status,
|
1199
|
+
orjson.dumps(data),
|
1200
|
+
)
|
1201
|
+
|
1202
|
+
if response.status != HTTPStatus.OK:
|
1203
|
+
_LOGGER.debug("Failed to refresh data")
|
1204
|
+
return False, {}
|
1205
|
+
|
1206
|
+
json_response = await response.json()
|
1207
|
+
_LOGGER.debug("Refresh data json:\n%s ", json_response)
|
1208
|
+
|
1209
|
+
if data_type == REFRESH_ACCESS_TOKEN and (
|
1210
|
+
new_token := json_response.get(REFRESH_ACCESS_TOKEN)
|
1211
|
+
):
|
1212
|
+
self._login_stored_data[REFRESH_ACCESS_TOKEN] = new_token
|
1213
|
+
self.expires_in = datetime.now(tz=UTC).timestamp() + int(
|
1214
|
+
json_response.get("expires_in")
|
1215
|
+
)
|
1216
|
+
return True, json_response
|
1217
|
+
|
1218
|
+
if data_type == REFRESH_AUTH_COOKIES:
|
1219
|
+
return True, json_response
|
1220
|
+
|
1221
|
+
_LOGGER.debug("Unexpected refresh data response")
|
1222
|
+
return False, {}
|
@@ -1,7 +1,6 @@
|
|
1
1
|
"""Constants for Amazon devices."""
|
2
2
|
|
3
3
|
import logging
|
4
|
-
from typing import Any
|
5
4
|
|
6
5
|
_LOGGER = logging.getLogger(__package__)
|
7
6
|
|
@@ -31,56 +30,6 @@ TO_REDACT = {
|
|
31
30
|
"user_id",
|
32
31
|
}
|
33
32
|
|
34
|
-
AMAZON_DE_OVERRIDE: dict[str, str] = {
|
35
|
-
"domain": "de",
|
36
|
-
"openid.assoc_handle": f"{DEFAULT_ASSOC_HANDLE}_de",
|
37
|
-
}
|
38
|
-
AMAZON_US_OVERRIDE: dict[str, str] = {
|
39
|
-
"domain": "com",
|
40
|
-
"openid.assoc_handle": DEFAULT_ASSOC_HANDLE,
|
41
|
-
}
|
42
|
-
|
43
|
-
DOMAIN_BY_ISO3166_COUNTRY: dict[str, dict[str, Any]] = {
|
44
|
-
"ar": AMAZON_US_OVERRIDE,
|
45
|
-
"at": AMAZON_DE_OVERRIDE,
|
46
|
-
"au": {
|
47
|
-
"domain": "com.au",
|
48
|
-
"openid.assoc_handle": f"{DEFAULT_ASSOC_HANDLE}_au",
|
49
|
-
},
|
50
|
-
"be": {
|
51
|
-
"domain": "com.be",
|
52
|
-
},
|
53
|
-
"br": AMAZON_US_OVERRIDE | {"market": "https://www.amazon.com.br"},
|
54
|
-
"gb": {
|
55
|
-
"domain": "co.uk",
|
56
|
-
"openid.assoc_handle": f"{DEFAULT_ASSOC_HANDLE}_uk",
|
57
|
-
},
|
58
|
-
"il": AMAZON_US_OVERRIDE,
|
59
|
-
"jp": {
|
60
|
-
"domain": "co.jp",
|
61
|
-
},
|
62
|
-
"mx": {
|
63
|
-
"domain": "com.mx",
|
64
|
-
},
|
65
|
-
"nl": {
|
66
|
-
"domain": "nl",
|
67
|
-
"market": "https://www.amazon.co.uk",
|
68
|
-
},
|
69
|
-
"no": AMAZON_DE_OVERRIDE,
|
70
|
-
"nz": {
|
71
|
-
"domain": "com.au",
|
72
|
-
"openid.assoc_handle": f"{DEFAULT_ASSOC_HANDLE}_au",
|
73
|
-
},
|
74
|
-
"pl": AMAZON_US_OVERRIDE,
|
75
|
-
"tr": {
|
76
|
-
"domain": "com.tr",
|
77
|
-
},
|
78
|
-
"us": AMAZON_US_OVERRIDE,
|
79
|
-
"za": {
|
80
|
-
"domain": "co.za",
|
81
|
-
},
|
82
|
-
}
|
83
|
-
|
84
33
|
# Amazon APP info
|
85
34
|
AMAZON_APP_BUNDLE_ID = "com.amazon.echo"
|
86
35
|
AMAZON_APP_ID = "MAPiOSLib/6.0/ToHideRetailLink"
|
@@ -90,6 +39,7 @@ AMAZON_DEVICE_SOFTWARE_VERSION = "35602678"
|
|
90
39
|
AMAZON_DEVICE_TYPE = "A2IVLV5VM2W81"
|
91
40
|
AMAZON_CLIENT_OS = "18.5"
|
92
41
|
|
42
|
+
DEFAULT_SITE = "https://www.amazon.com"
|
93
43
|
DEFAULT_HEADERS = {
|
94
44
|
"User-Agent": (
|
95
45
|
f"AmazonWebView/AmazonAlexa/{AMAZON_APP_VERSION}/iOS/{AMAZON_CLIENT_OS}/iPhone"
|
@@ -101,6 +51,9 @@ DEFAULT_HEADERS = {
|
|
101
51
|
DEFAULT_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0" # noqa: E501
|
102
52
|
CSRF_COOKIE = "csrf"
|
103
53
|
|
54
|
+
REFRESH_ACCESS_TOKEN = "access_token" # noqa: S105
|
55
|
+
REFRESH_AUTH_COOKIES = "auth_cookies"
|
56
|
+
|
104
57
|
NODE_DEVICES = "devices"
|
105
58
|
NODE_DO_NOT_DISTURB = "doNotDisturbDeviceStatusList"
|
106
59
|
NODE_PREFERENCES = "devicePreferences"
|
@@ -228,6 +181,11 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
|
|
228
181
|
"model": "FireTV 4k MAX",
|
229
182
|
"hw_version": "Gen2",
|
230
183
|
},
|
184
|
+
"A1X92YQU8MWAPD": {
|
185
|
+
"manufacturer": "Devialet",
|
186
|
+
"model": "Freebox Delta",
|
187
|
+
"hw_version": None,
|
188
|
+
},
|
231
189
|
"A1XWJRHALS1REP": {
|
232
190
|
"model": "Echo Show 5",
|
233
191
|
"hw_version": "Gen2",
|
@@ -379,7 +337,7 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
|
|
379
337
|
"hw_version": None,
|
380
338
|
},
|
381
339
|
"A3RMGO6LYLH7YN": {
|
382
|
-
"model": "Echo
|
340
|
+
"model": "Echo",
|
383
341
|
"hw_version": "Gen4",
|
384
342
|
},
|
385
343
|
"A3S5BH2HU6VAYF": {
|
@@ -411,6 +369,10 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
|
|
411
369
|
"model": "Echo",
|
412
370
|
"hw_version": "Gen2",
|
413
371
|
},
|
372
|
+
"ADMKNMEVNL158": {
|
373
|
+
"model": "Echo Hub",
|
374
|
+
"hw_version": "Gen1",
|
375
|
+
},
|
414
376
|
"ADOUDFQX2QVX0": {
|
415
377
|
"model": "Fire TV Omni QLED",
|
416
378
|
"hw_version": None,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|