aioamazondevices 3.1.19__py3-none-any.whl → 3.1.23__py3-none-any.whl
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/__init__.py +1 -1
- aioamazondevices/api.py +56 -26
- aioamazondevices/const.py +2 -1
- aioamazondevices/utils.py +3 -1
- {aioamazondevices-3.1.19.dist-info → aioamazondevices-3.1.23.dist-info}/METADATA +1 -1
- aioamazondevices-3.1.23.dist-info/RECORD +11 -0
- aioamazondevices-3.1.19.dist-info/RECORD +0 -11
- {aioamazondevices-3.1.19.dist-info → aioamazondevices-3.1.23.dist-info}/LICENSE +0 -0
- {aioamazondevices-3.1.19.dist-info → aioamazondevices-3.1.23.dist-info}/WHEEL +0 -0
aioamazondevices/__init__.py
CHANGED
aioamazondevices/api.py
CHANGED
@@ -146,6 +146,15 @@ class AmazonEchoApi:
|
|
146
146
|
self.session: ClientSession
|
147
147
|
self._devices: dict[str, Any] = {}
|
148
148
|
|
149
|
+
lang_object = Language.make(territory=self._login_country_code.upper())
|
150
|
+
lang_maximized = lang_object.maximize()
|
151
|
+
self._language = f"{lang_maximized.language}-{lang_maximized.region}"
|
152
|
+
_LOGGER.debug(
|
153
|
+
"Initialize library with domain <%s> and language <%s>",
|
154
|
+
self._domain,
|
155
|
+
self._language,
|
156
|
+
)
|
157
|
+
|
149
158
|
def _load_website_cookies(self) -> dict[str, str]:
|
150
159
|
"""Get website cookies, if avaliables."""
|
151
160
|
if not self._login_stored_data:
|
@@ -210,22 +219,23 @@ class AmazonEchoApi:
|
|
210
219
|
code_challenge = self._create_s256_code_challenge(code_verifier)
|
211
220
|
|
212
221
|
oauth_params = {
|
213
|
-
"openid.oa2.response_type": "code",
|
214
|
-
"openid.oa2.code_challenge_method": "S256",
|
215
|
-
"openid.oa2.code_challenge": code_challenge,
|
216
222
|
"openid.return_to": f"https://www.amazon.{self._domain}/ap/maplanding",
|
223
|
+
"openid.oa2.code_challenge_method": "S256",
|
217
224
|
"openid.assoc_handle": self._assoc_handle,
|
218
225
|
"openid.identity": "http://specs.openid.net/auth/2.0/identifier_select",
|
226
|
+
"pageId": self._assoc_handle,
|
219
227
|
"accountStatusPolicy": "P1",
|
220
228
|
"openid.claimed_id": "http://specs.openid.net/auth/2.0/identifier_select",
|
221
229
|
"openid.mode": "checkid_setup",
|
222
230
|
"openid.ns.oa2": "http://www.amazon.com/ap/ext/oauth/2",
|
223
231
|
"openid.oa2.client_id": f"device:{client_id}",
|
232
|
+
"language": self._language.replace("-", "_"),
|
224
233
|
"openid.ns.pape": "http://specs.openid.net/extensions/pape/1.0",
|
234
|
+
"openid.oa2.code_challenge": code_challenge,
|
225
235
|
"openid.oa2.scope": "device_auth_access",
|
226
|
-
"forceMobileLayout": "true",
|
227
236
|
"openid.ns": "http://specs.openid.net/auth/2.0",
|
228
237
|
"openid.pape.max_auth_age": "0",
|
238
|
+
"openid.oa2.response_type": "code",
|
229
239
|
}
|
230
240
|
|
231
241
|
return (
|
@@ -273,8 +283,10 @@ class AmazonEchoApi:
|
|
273
283
|
"""Create HTTP client session."""
|
274
284
|
if not hasattr(self, "session") or self.session.closed:
|
275
285
|
_LOGGER.debug("Creating HTTP session (aiohttp)")
|
286
|
+
headers = DEFAULT_HEADERS
|
287
|
+
headers.update({"Accept-Language": self._language})
|
276
288
|
self.session = ClientSession(
|
277
|
-
headers=
|
289
|
+
headers=headers,
|
278
290
|
cookies=self._cookies,
|
279
291
|
)
|
280
292
|
|
@@ -323,6 +335,7 @@ class AmazonEchoApi:
|
|
323
335
|
)
|
324
336
|
|
325
337
|
headers = DEFAULT_HEADERS
|
338
|
+
headers.update({"Accept-Language": self._language})
|
326
339
|
if self._csrf_cookie and CSRF_COOKIE not in headers:
|
327
340
|
csrf = {CSRF_COOKIE: self._csrf_cookie}
|
328
341
|
_LOGGER.debug("Adding <%s> to headers", csrf)
|
@@ -479,11 +492,6 @@ class AmazonEchoApi:
|
|
479
492
|
)
|
480
493
|
raise CannotRegisterDevice(f"{HTTPStatus(resp.status).phrase}: {msg}")
|
481
494
|
|
482
|
-
await self._save_to_file(
|
483
|
-
await resp.text(),
|
484
|
-
url=register_url,
|
485
|
-
extension=JSON_EXTENSION,
|
486
|
-
)
|
487
495
|
success_response = resp_json["response"]["success"]
|
488
496
|
|
489
497
|
tokens = success_response["tokens"]
|
@@ -530,16 +538,43 @@ class AmazonEchoApi:
|
|
530
538
|
location_details = network_detail["locationDetails"]["locationDetails"]
|
531
539
|
default_location = location_details["Default_Location"]
|
532
540
|
amazon_bridge = default_location["amazonBridgeDetails"]["amazonBridgeDetails"]
|
533
|
-
lambda_bridge = amazon_bridge.get("LambdaBridge_AAA/SonarCloudService")
|
534
|
-
if not lambda_bridge:
|
535
|
-
# Some very old devices lack the key for sensors data
|
536
|
-
return []
|
537
|
-
appliance_details = lambda_bridge["applianceDetails"]["applianceDetails"]
|
538
541
|
|
542
|
+
# New devices are based on LambdaBridge_AAA structure
|
543
|
+
lambda_bridge_aaa = amazon_bridge.get("LambdaBridge_AAA/SonarCloudService")
|
544
|
+
appliance_details_aaa = (
|
545
|
+
lambda_bridge_aaa["applianceDetails"]["applianceDetails"]
|
546
|
+
if lambda_bridge_aaa
|
547
|
+
else {}
|
548
|
+
)
|
549
|
+
|
550
|
+
entity_ids_list: list[dict[str, str]] = await self._get_entities_ids(
|
551
|
+
appliance_details_aaa, "AAA_SonarCloudService"
|
552
|
+
)
|
553
|
+
|
554
|
+
# Old devices are based on LambdaBridge_AlexaBridge structure
|
555
|
+
for bridge_key, bridge_value in amazon_bridge.items():
|
556
|
+
if "LambdaBridge_AlexaBridge/" in bridge_key:
|
557
|
+
# Value key: "LambdaBridge_AlexaBridge/XXXXXXXXXXXXXX@XXXXXXXXXXXXXX"
|
558
|
+
# Value subkey: "AlexaBridge_XXXXXXXXXXXXXX@XXXXXXXXXXXXXX_XXXXXXXXXXXX"
|
559
|
+
subkey = bridge_key.split("_")[1].replace("/", "_")
|
560
|
+
|
561
|
+
appliance_details_alexa = bridge_value["applianceDetails"][
|
562
|
+
"applianceDetails"
|
563
|
+
]
|
564
|
+
entity_ids_list.extend(
|
565
|
+
await self._get_entities_ids(appliance_details_alexa, subkey)
|
566
|
+
)
|
567
|
+
|
568
|
+
return entity_ids_list
|
569
|
+
|
570
|
+
async def _get_entities_ids(
|
571
|
+
self, appliance_details: dict[str, Any], searchkey: str
|
572
|
+
) -> list[dict[str, str]]:
|
573
|
+
"""Extract entityId and applianceId."""
|
539
574
|
entity_ids_list: list[dict[str, str]] = []
|
540
|
-
# Process each appliance that starts with
|
575
|
+
# Process each appliance that starts with "searchkey"
|
541
576
|
for appliance_key, appliance_data in appliance_details.items():
|
542
|
-
if not appliance_key.startswith(
|
577
|
+
if not appliance_key.startswith(searchkey):
|
543
578
|
continue
|
544
579
|
|
545
580
|
entity_id = appliance_data["entityId"]
|
@@ -662,7 +697,7 @@ class AmazonEchoApi:
|
|
662
697
|
register_device = await self._register_device(device_login_data)
|
663
698
|
self._login_stored_data = register_device
|
664
699
|
|
665
|
-
_LOGGER.info("Register device: %s", register_device)
|
700
|
+
_LOGGER.info("Register device: %s", scrub_fields(register_device))
|
666
701
|
return register_device
|
667
702
|
|
668
703
|
async def login_mode_stored_data(self) -> dict[str, Any]:
|
@@ -704,7 +739,6 @@ class AmazonEchoApi:
|
|
704
739
|
_LOGGER.debug("Response code: |%s|", response_code)
|
705
740
|
|
706
741
|
response_data = await raw_resp.text()
|
707
|
-
_LOGGER.debug("Response data: |%s|", response_data)
|
708
742
|
|
709
743
|
if not self._csrf_cookie:
|
710
744
|
self._csrf_cookie = raw_resp.cookies.get(CSRF_COOKIE, Morsel()).value
|
@@ -712,7 +746,7 @@ class AmazonEchoApi:
|
|
712
746
|
|
713
747
|
json_data = {} if len(response_data) == 0 else await raw_resp.json()
|
714
748
|
|
715
|
-
_LOGGER.debug("JSON data: |%s|", json_data)
|
749
|
+
_LOGGER.debug("JSON data: |%s|", scrub_fields(json_data))
|
716
750
|
|
717
751
|
for data in json_data[key]:
|
718
752
|
dev_serial = data.get("serialNumber") or data.get("deviceSerialNumber")
|
@@ -821,10 +855,6 @@ class AmazonEchoApi:
|
|
821
855
|
message_source: AmazonMusicSource | None = None,
|
822
856
|
) -> None:
|
823
857
|
"""Send message to specific device."""
|
824
|
-
lang_object = Language.make(territory=self._login_country_code.upper())
|
825
|
-
lang_maximized = lang_object.maximize()
|
826
|
-
locale = f"{lang_maximized.language}-{lang_maximized.region}"
|
827
|
-
|
828
858
|
if not self._login_stored_data:
|
829
859
|
_LOGGER.warning("Trying to send message before login")
|
830
860
|
return
|
@@ -832,7 +862,7 @@ class AmazonEchoApi:
|
|
832
862
|
base_payload = {
|
833
863
|
"deviceType": device.device_type,
|
834
864
|
"deviceSerialNumber": device.serial_number,
|
835
|
-
"locale":
|
865
|
+
"locale": self._language,
|
836
866
|
"customerId": device.device_owner_customer_id,
|
837
867
|
}
|
838
868
|
|
@@ -867,7 +897,7 @@ class AmazonEchoApi:
|
|
867
897
|
"expireAfter": "PT5S",
|
868
898
|
"content": [
|
869
899
|
{
|
870
|
-
"locale":
|
900
|
+
"locale": self._language,
|
871
901
|
"display": {
|
872
902
|
"title": "Home Assistant",
|
873
903
|
"body": message_body,
|
aioamazondevices/const.py
CHANGED
@@ -7,11 +7,13 @@ _LOGGER = logging.getLogger(__package__)
|
|
7
7
|
DEFAULT_ASSOC_HANDLE = "amzn_dp_project_dee_ios"
|
8
8
|
|
9
9
|
TO_REDACT = {
|
10
|
+
"address",
|
10
11
|
"address1",
|
11
12
|
"address2",
|
12
13
|
"address3",
|
13
14
|
"city",
|
14
15
|
"county",
|
16
|
+
"customerId",
|
15
17
|
"deviceAccountId",
|
16
18
|
"deviceAddress",
|
17
19
|
"deviceOwnerCustomerId",
|
@@ -82,7 +84,6 @@ DEFAULT_HEADERS = {
|
|
82
84
|
),
|
83
85
|
"Accept-Charset": "utf-8",
|
84
86
|
"Accept-Encoding": "gzip",
|
85
|
-
"Accept-Language": "en-US",
|
86
87
|
"Connection": "keep-alive",
|
87
88
|
}
|
88
89
|
CSRF_COOKIE = "csrf"
|
aioamazondevices/utils.py
CHANGED
@@ -37,7 +37,9 @@ def scrub_fields(
|
|
37
37
|
result = {}
|
38
38
|
for k, v in obj.items():
|
39
39
|
# If the key itself is sensitive → overwrite its value
|
40
|
-
if k
|
40
|
+
if k == "email":
|
41
|
+
result[k] = obfuscate_email(v)
|
42
|
+
elif k in field_names:
|
41
43
|
result[k] = replacement
|
42
44
|
else:
|
43
45
|
# Otherwise keep walking
|
@@ -0,0 +1,11 @@
|
|
1
|
+
aioamazondevices/__init__.py,sha256=mZIaRiUx4hb3bx5kIhZc9PX7hxD-qnhod-h81ENTXac,277
|
2
|
+
aioamazondevices/api.py,sha256=J4e85fBrHIWPeTvP7TYsezvdHKQ0I_Pqz3HCcdmF6J8,37542
|
3
|
+
aioamazondevices/const.py,sha256=J47aHAfoA6rRgHDvn-q_S4xp8MrC6O1rEk1ZeRnN-8E,8529
|
4
|
+
aioamazondevices/exceptions.py,sha256=JDnSFi_7oEhqK31sHXf0S_cyMoMjiRJuLp4ow7mYgLY,643
|
5
|
+
aioamazondevices/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
aioamazondevices/sounds.py,sha256=01pVCDFIuhrLypXInw4JNuHsC6zjMLsuKocet1R6we8,13409
|
7
|
+
aioamazondevices/utils.py,sha256=RzuKRhnq_8ymCoJMoQJ2vBYyuew06RSWpqQWmqdNczE,2019
|
8
|
+
aioamazondevices-3.1.23.dist-info/LICENSE,sha256=sS48k5sp9bFV-NSHDfAJuTZZ_-AP9ZDqUzQ9sffGlsg,11346
|
9
|
+
aioamazondevices-3.1.23.dist-info/METADATA,sha256=xEheLDnUfjp1pQbgAMvDsWmOcG5xnDnU8eZfmYSWg4A,5235
|
10
|
+
aioamazondevices-3.1.23.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
11
|
+
aioamazondevices-3.1.23.dist-info/RECORD,,
|
@@ -1,11 +0,0 @@
|
|
1
|
-
aioamazondevices/__init__.py,sha256=yYJH7153YmQfYcpIzSJNgHgxNQI7bZK5Aq0emVmfVQg,277
|
2
|
-
aioamazondevices/api.py,sha256=7IJF_bOSs0C1fLKeOR2Y87qyADlfGEGWlihZ2vlLaL8,36250
|
3
|
-
aioamazondevices/const.py,sha256=KQbHCOvzBTcWMc3O-kLR72wJJDBbrFK9dPHJVEPMY4M,8528
|
4
|
-
aioamazondevices/exceptions.py,sha256=JDnSFi_7oEhqK31sHXf0S_cyMoMjiRJuLp4ow7mYgLY,643
|
5
|
-
aioamazondevices/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
aioamazondevices/sounds.py,sha256=01pVCDFIuhrLypXInw4JNuHsC6zjMLsuKocet1R6we8,13409
|
7
|
-
aioamazondevices/utils.py,sha256=1PrzAtQHigN2rGxdMVPKzbjOnwh-MLc9YWkQaVQIHT4,1941
|
8
|
-
aioamazondevices-3.1.19.dist-info/LICENSE,sha256=sS48k5sp9bFV-NSHDfAJuTZZ_-AP9ZDqUzQ9sffGlsg,11346
|
9
|
-
aioamazondevices-3.1.19.dist-info/METADATA,sha256=eOPZwC2lraH2xYJgydkqHCnPoe96hYBy0Qmua9AltY8,5235
|
10
|
-
aioamazondevices-3.1.19.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
11
|
-
aioamazondevices-3.1.19.dist-info/RECORD,,
|
File without changes
|
File without changes
|