aioamazondevices 1.4.1__py3-none-any.whl → 1.5.0__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 +118 -47
- {aioamazondevices-1.4.1.dist-info → aioamazondevices-1.5.0.dist-info}/METADATA +3 -2
- aioamazondevices-1.5.0.dist-info/RECORD +9 -0
- {aioamazondevices-1.4.1.dist-info → aioamazondevices-1.5.0.dist-info}/WHEEL +1 -1
- aioamazondevices-1.4.1.dist-info/RECORD +0 -9
- {aioamazondevices-1.4.1.dist-info → aioamazondevices-1.5.0.dist-info}/LICENSE +0 -0
aioamazondevices/__init__.py
CHANGED
aioamazondevices/api.py
CHANGED
@@ -8,7 +8,7 @@ import uuid
|
|
8
8
|
from dataclasses import dataclass
|
9
9
|
from datetime import UTC, datetime, timedelta
|
10
10
|
from http import HTTPStatus
|
11
|
-
from http.cookies import
|
11
|
+
from http.cookies import Morsel
|
12
12
|
from pathlib import Path
|
13
13
|
from typing import Any, cast
|
14
14
|
from urllib.parse import parse_qs, urlencode
|
@@ -54,6 +54,7 @@ class AmazonDevice:
|
|
54
54
|
device_family: str
|
55
55
|
device_type: str
|
56
56
|
device_owner_customer_id: str
|
57
|
+
device_cluster_members: list[str]
|
57
58
|
online: bool
|
58
59
|
serial_number: str
|
59
60
|
software_version: str
|
@@ -96,15 +97,16 @@ class AmazonEchoApi:
|
|
96
97
|
self._save_raw_data = save_raw_data
|
97
98
|
self._login_stored_data = login_data
|
98
99
|
self._serial = self._serial_number()
|
100
|
+
self._list_for_clusters: dict[str, str] = {}
|
99
101
|
|
100
102
|
self.session: ClientSession
|
101
103
|
|
102
|
-
def _load_website_cookies(self) ->
|
104
|
+
def _load_website_cookies(self) -> dict[str, str]:
|
103
105
|
"""Get website cookies, if avaliables."""
|
104
106
|
if not self._login_stored_data:
|
105
|
-
return
|
107
|
+
return {}
|
106
108
|
|
107
|
-
return cast("
|
109
|
+
return cast("dict", self._login_stored_data["website_cookies"])
|
108
110
|
|
109
111
|
def _serial_number(self) -> str:
|
110
112
|
"""Get or calculate device serial number."""
|
@@ -255,7 +257,6 @@ class AmazonEchoApi:
|
|
255
257
|
data=input_data if not json_data else orjson.dumps(input_data),
|
256
258
|
cookies=self._load_website_cookies(),
|
257
259
|
headers=headers,
|
258
|
-
allow_redirects=True,
|
259
260
|
)
|
260
261
|
content_type: str = resp.headers.get("Content-Type", "")
|
261
262
|
_LOGGER.debug(
|
@@ -363,7 +364,7 @@ class AmazonEchoApi:
|
|
363
364
|
input_data=body,
|
364
365
|
json_data=True,
|
365
366
|
)
|
366
|
-
resp_json = resp.json()
|
367
|
+
resp_json = await resp.json()
|
367
368
|
|
368
369
|
if resp.status != HTTPStatus.OK:
|
369
370
|
_LOGGER.error(
|
@@ -374,7 +375,7 @@ class AmazonEchoApi:
|
|
374
375
|
raise CannotRegisterDevice(resp_json)
|
375
376
|
|
376
377
|
await self._save_to_file(
|
377
|
-
resp.text,
|
378
|
+
await resp.text(),
|
378
379
|
url=register_url,
|
379
380
|
extension=JSON_EXTENSION,
|
380
381
|
)
|
@@ -521,7 +522,8 @@ class AmazonEchoApi:
|
|
521
522
|
_LOGGER.debug("Response data: |%s|", response_data)
|
522
523
|
|
523
524
|
if not self._csrf_cookie:
|
524
|
-
self._csrf_cookie = raw_resp.cookies.get(CSRF_COOKIE).value
|
525
|
+
self._csrf_cookie = raw_resp.cookies.get(CSRF_COOKIE, Morsel()).value
|
526
|
+
_LOGGER.error("CSRF cookie value: <%s>", self._csrf_cookie)
|
525
527
|
|
526
528
|
json_data = {} if len(response_data) == 0 else await raw_resp.json()
|
527
529
|
|
@@ -536,29 +538,44 @@ class AmazonEchoApi:
|
|
536
538
|
|
537
539
|
final_devices_list: dict[str, AmazonDevice] = {}
|
538
540
|
for device in devices.values():
|
541
|
+
devices_node = device[NODE_DEVICES]
|
542
|
+
preferences_node = device.get(NODE_PREFERENCES)
|
543
|
+
do_not_disturb_node = device[NODE_DO_NOT_DISTURB]
|
544
|
+
bluetooth_node = device[NODE_BLUETOOTH]
|
539
545
|
# Remove stale, orphaned and virtual devices
|
540
546
|
if (
|
541
547
|
NODE_DEVICES not in device
|
542
|
-
or
|
548
|
+
or devices_node.get("deviceType") == AMAZON_DEVICE_TYPE
|
543
549
|
):
|
544
550
|
continue
|
545
551
|
|
546
|
-
serial_number: str =
|
547
|
-
preferences = device.get(NODE_PREFERENCES)
|
552
|
+
serial_number: str = devices_node["serialNumber"]
|
548
553
|
final_devices_list[serial_number] = AmazonDevice(
|
549
|
-
account_name=
|
550
|
-
capabilities=
|
551
|
-
device_family=
|
552
|
-
device_type=
|
553
|
-
device_owner_customer_id=
|
554
|
-
|
554
|
+
account_name=devices_node["accountName"],
|
555
|
+
capabilities=devices_node["capabilities"],
|
556
|
+
device_family=devices_node["deviceFamily"],
|
557
|
+
device_type=devices_node["deviceType"],
|
558
|
+
device_owner_customer_id=devices_node["deviceOwnerCustomerId"],
|
559
|
+
device_cluster_members=(
|
560
|
+
devices_node["clusterMembers"] or [serial_number]
|
561
|
+
),
|
562
|
+
online=devices_node["online"],
|
555
563
|
serial_number=serial_number,
|
556
|
-
software_version=
|
557
|
-
do_not_disturb=
|
558
|
-
response_style=
|
559
|
-
|
564
|
+
software_version=devices_node["softwareVersion"],
|
565
|
+
do_not_disturb=do_not_disturb_node["enabled"],
|
566
|
+
response_style=(
|
567
|
+
preferences_node["responseStyle"] if preferences_node else None
|
568
|
+
),
|
569
|
+
bluetooth_state=bluetooth_node["online"],
|
560
570
|
)
|
561
571
|
|
572
|
+
self._list_for_clusters.update(
|
573
|
+
{
|
574
|
+
device.serial_number: device.device_type
|
575
|
+
for device in final_devices_list.values()
|
576
|
+
}
|
577
|
+
)
|
578
|
+
|
562
579
|
return final_devices_list
|
563
580
|
|
564
581
|
async def auth_check_status(self) -> bool:
|
@@ -583,18 +600,71 @@ class AmazonEchoApi:
|
|
583
600
|
_LOGGER.debug("Session authenticated: %s", authenticated)
|
584
601
|
return bool(authenticated)
|
585
602
|
|
586
|
-
async def
|
587
|
-
self,
|
588
|
-
|
589
|
-
|
590
|
-
) -> dict[str, Any]:
|
591
|
-
"""Call Alexa.Speak to send a message."""
|
603
|
+
async def _send_message(
|
604
|
+
self, device: AmazonDevice, message_type: str, message_body: str
|
605
|
+
) -> None:
|
606
|
+
"""Send message to specific device."""
|
592
607
|
locale_data = Locale.parse(f"und_{self._login_country_code}")
|
593
608
|
locale = f"{locale_data.language}-{locale_data.language}"
|
594
609
|
|
595
610
|
if not self._login_stored_data:
|
596
611
|
_LOGGER.warning("Trying to send message before login")
|
597
|
-
return
|
612
|
+
return
|
613
|
+
|
614
|
+
payload: dict[str, Any]
|
615
|
+
if message_type == "Alexa.Speak":
|
616
|
+
payload = {
|
617
|
+
"deviceType": device.device_type,
|
618
|
+
"deviceSerialNumber": device.serial_number,
|
619
|
+
"locale": locale,
|
620
|
+
"customerId": device.device_owner_customer_id,
|
621
|
+
"textToSpeak": message_body,
|
622
|
+
"target": {
|
623
|
+
"customerId": device.device_owner_customer_id,
|
624
|
+
"devices": [
|
625
|
+
{
|
626
|
+
"deviceSerialNumber": device.serial_number,
|
627
|
+
"deviceTypeId": device.device_type,
|
628
|
+
},
|
629
|
+
],
|
630
|
+
},
|
631
|
+
"skillId": "amzn1.ask.1p.saysomething",
|
632
|
+
}
|
633
|
+
else:
|
634
|
+
playback_devices: list[dict[str, str]] = [
|
635
|
+
{
|
636
|
+
"deviceSerialNumber": serial,
|
637
|
+
"deviceTypeId": self._list_for_clusters[serial],
|
638
|
+
}
|
639
|
+
for serial in device.device_cluster_members
|
640
|
+
if serial in self._list_for_clusters
|
641
|
+
]
|
642
|
+
|
643
|
+
payload = {
|
644
|
+
"deviceType": device.device_type,
|
645
|
+
"deviceSerialNumber": device.serial_number,
|
646
|
+
"locale": locale,
|
647
|
+
"customerId": device.device_owner_customer_id,
|
648
|
+
"expireAfter": "PT5S",
|
649
|
+
"content": [
|
650
|
+
{
|
651
|
+
"locale": locale,
|
652
|
+
"display": {
|
653
|
+
"title": "Home Assistant",
|
654
|
+
"body": message_body,
|
655
|
+
},
|
656
|
+
"speak": {
|
657
|
+
"type": "text",
|
658
|
+
"value": message_body,
|
659
|
+
},
|
660
|
+
}
|
661
|
+
],
|
662
|
+
"target": {
|
663
|
+
"customerId": device.device_owner_customer_id,
|
664
|
+
"devices": playback_devices,
|
665
|
+
},
|
666
|
+
"skillId": "amzn1.ask.1p.routines.messaging",
|
667
|
+
}
|
598
668
|
|
599
669
|
sequence = {
|
600
670
|
"@type": "com.amazon.alexa.behaviors.model.Sequence",
|
@@ -603,28 +673,13 @@ class AmazonEchoApi:
|
|
603
673
|
"nodesToExecute": [
|
604
674
|
{
|
605
675
|
"@type": "com.amazon.alexa.behaviors.model.OpaquePayloadOperationNode", # noqa: E501
|
606
|
-
"type":
|
607
|
-
"operationPayload":
|
608
|
-
"deviceType": device.device_type,
|
609
|
-
"deviceSerialNumber": device.serial_number,
|
610
|
-
"locale": locale,
|
611
|
-
"customerId": device.device_owner_customer_id,
|
612
|
-
"textToSpeak": message_body,
|
613
|
-
"target": {
|
614
|
-
"customerId": device.device_owner_customer_id,
|
615
|
-
"devices": [
|
616
|
-
{
|
617
|
-
"deviceSerialNumber": device.serial_number,
|
618
|
-
"deviceTypeId": device.device_type,
|
619
|
-
},
|
620
|
-
],
|
621
|
-
},
|
622
|
-
"skillId": "amzn1.ask.1p.saysomething",
|
623
|
-
},
|
676
|
+
"type": message_type,
|
677
|
+
"operationPayload": payload,
|
624
678
|
},
|
625
679
|
],
|
626
680
|
},
|
627
681
|
}
|
682
|
+
|
628
683
|
node_data = {
|
629
684
|
"behaviorId": "PREVIEW",
|
630
685
|
"sequenceJson": orjson.dumps(sequence).decode("utf-8"),
|
@@ -639,4 +694,20 @@ class AmazonEchoApi:
|
|
639
694
|
json_data=True,
|
640
695
|
)
|
641
696
|
|
642
|
-
return
|
697
|
+
return
|
698
|
+
|
699
|
+
async def call_alexa_speak(
|
700
|
+
self,
|
701
|
+
device: AmazonDevice,
|
702
|
+
message_body: str,
|
703
|
+
) -> None:
|
704
|
+
"""Call Alexa.Speak to send a message."""
|
705
|
+
return await self._send_message(device, "Alexa.Speak", message_body)
|
706
|
+
|
707
|
+
async def call_alexa_announcement(
|
708
|
+
self,
|
709
|
+
device: AmazonDevice,
|
710
|
+
message_body: str,
|
711
|
+
) -> None:
|
712
|
+
"""Call AlexaAnnouncement to send a message."""
|
713
|
+
return await self._send_message(device, "AlexaAnnouncement", message_body)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: aioamazondevices
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.5.0
|
4
4
|
Summary: Python library to control Amazon devices
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: Simone Chemelli
|
@@ -82,7 +82,8 @@ The script accept command line arguments or a library_test.json config file:
|
|
82
82
|
"country": "IT",
|
83
83
|
"email": "<my_address@gmail.com>",
|
84
84
|
"password": "<my_password>",
|
85
|
-
"
|
85
|
+
"device_name_speak": "Echo Dot Livingroom",
|
86
|
+
"device_name_announcement": "Everywhere",
|
86
87
|
"login_data_file": "out/login_data.json",
|
87
88
|
"save_raw_data": "True"
|
88
89
|
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
aioamazondevices/__init__.py,sha256=c7Bi99W-tVQoCNwjK33ROJQCAZG1GcwSjaCvz9kMOwM,276
|
2
|
+
aioamazondevices/api.py,sha256=_4kojHD_86milDMLrbVscHgNg9nerq8mE6k7xOXOAeE,25874
|
3
|
+
aioamazondevices/const.py,sha256=6BBEg_q2BkYVYJcz3hMrLNyEwOJBWziPSStMzftWQLg,2106
|
4
|
+
aioamazondevices/exceptions.py,sha256=qK_Hak9pc-lC2FPW-0i4rYIwNpEOHMmA9Rii8F2lkQo,1260
|
5
|
+
aioamazondevices/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
aioamazondevices-1.5.0.dist-info/LICENSE,sha256=sS48k5sp9bFV-NSHDfAJuTZZ_-AP9ZDqUzQ9sffGlsg,11346
|
7
|
+
aioamazondevices-1.5.0.dist-info/METADATA,sha256=m03j63zvtCHK_AWQbVBSaUsB5yRhlZTSWVPtZ389xdo,5014
|
8
|
+
aioamazondevices-1.5.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
9
|
+
aioamazondevices-1.5.0.dist-info/RECORD,,
|
@@ -1,9 +0,0 @@
|
|
1
|
-
aioamazondevices/__init__.py,sha256=ilLSTYfBxHU79HSyEtxbxYfY0tiPoQvOGm2HAx1zVm8,276
|
2
|
-
aioamazondevices/api.py,sha256=FLugYMynG9po9W-pwSM0ppNM-TCM2Dz3u4Ygo9fXFgI,23556
|
3
|
-
aioamazondevices/const.py,sha256=6BBEg_q2BkYVYJcz3hMrLNyEwOJBWziPSStMzftWQLg,2106
|
4
|
-
aioamazondevices/exceptions.py,sha256=qK_Hak9pc-lC2FPW-0i4rYIwNpEOHMmA9Rii8F2lkQo,1260
|
5
|
-
aioamazondevices/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
aioamazondevices-1.4.1.dist-info/LICENSE,sha256=sS48k5sp9bFV-NSHDfAJuTZZ_-AP9ZDqUzQ9sffGlsg,11346
|
7
|
-
aioamazondevices-1.4.1.dist-info/METADATA,sha256=d1dxm4-5H-J3mvA7i0diHn0gyKQF4QwqkxPjCPApJ9A,4964
|
8
|
-
aioamazondevices-1.4.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
9
|
-
aioamazondevices-1.4.1.dist-info/RECORD,,
|
File without changes
|