aioamazondevices 6.5.4__tar.gz → 8.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-6.5.4 → aioamazondevices-8.0.1}/PKG-INFO +10 -4
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/README.md +9 -3
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/pyproject.toml +1 -1
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/src/aioamazondevices/__init__.py +1 -1
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/src/aioamazondevices/api.py +42 -167
- aioamazondevices-8.0.1/src/aioamazondevices/const/__init__.py +1 -0
- aioamazondevices-6.5.4/src/aioamazondevices/const.py → aioamazondevices-8.0.1/src/aioamazondevices/const/devices.py +2 -169
- aioamazondevices-8.0.1/src/aioamazondevices/const/http.py +36 -0
- aioamazondevices-8.0.1/src/aioamazondevices/const/metadata.py +44 -0
- aioamazondevices-6.5.4/src/aioamazondevices/query.py → aioamazondevices-8.0.1/src/aioamazondevices/const/queries.py +1 -1
- aioamazondevices-8.0.1/src/aioamazondevices/const/schedules.py +61 -0
- {aioamazondevices-6.5.4/src/aioamazondevices → aioamazondevices-8.0.1/src/aioamazondevices/const}/sounds.py +2 -1
- aioamazondevices-8.0.1/src/aioamazondevices/structures.py +65 -0
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/src/aioamazondevices/utils.py +23 -1
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/LICENSE +0 -0
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/src/aioamazondevices/exceptions.py +0 -0
- {aioamazondevices-6.5.4 → aioamazondevices-8.0.1}/src/aioamazondevices/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aioamazondevices
|
|
3
|
-
Version:
|
|
3
|
+
Version: 8.0.1
|
|
4
4
|
Summary: Python library to control Amazon devices
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -81,7 +81,6 @@ The script accept command line arguments or a library_test.json config file:
|
|
|
81
81
|
"single_device_name": "Echo Dot Livingroom",
|
|
82
82
|
"cluster_device_name": "Everywhere",
|
|
83
83
|
"login_data_file": "out/login_data.json",
|
|
84
|
-
"save_raw_data": true,
|
|
85
84
|
"test": true
|
|
86
85
|
}
|
|
87
86
|
```
|
|
@@ -142,6 +141,15 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
142
141
|
<sub><b>Flo</b></sub>
|
|
143
142
|
</a>
|
|
144
143
|
</td>
|
|
144
|
+
<td align="center">
|
|
145
|
+
<a href="https://github.com/francescolf">
|
|
146
|
+
<img src="https://avatars.githubusercontent.com/u/14892143?v=4" width="100;" alt="francescolf"/>
|
|
147
|
+
<br />
|
|
148
|
+
<sub><b>Francesco Lo Faro</b></sub>
|
|
149
|
+
</a>
|
|
150
|
+
</td>
|
|
151
|
+
</tr>
|
|
152
|
+
<tr>
|
|
145
153
|
<td align="center">
|
|
146
154
|
<a href="https://github.com/lchavezcuu">
|
|
147
155
|
<img src="https://avatars.githubusercontent.com/u/22165856?v=4" width="100;" alt="lchavezcuu"/>
|
|
@@ -149,8 +157,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
149
157
|
<sub><b>Luis Chavez</b></sub>
|
|
150
158
|
</a>
|
|
151
159
|
</td>
|
|
152
|
-
</tr>
|
|
153
|
-
<tr>
|
|
154
160
|
<td align="center">
|
|
155
161
|
<a href="https://github.com/maxmati">
|
|
156
162
|
<img src="https://avatars.githubusercontent.com/u/509560?v=4" width="100;" alt="maxmati"/>
|
|
@@ -56,7 +56,6 @@ The script accept command line arguments or a library_test.json config file:
|
|
|
56
56
|
"single_device_name": "Echo Dot Livingroom",
|
|
57
57
|
"cluster_device_name": "Everywhere",
|
|
58
58
|
"login_data_file": "out/login_data.json",
|
|
59
|
-
"save_raw_data": true,
|
|
60
59
|
"test": true
|
|
61
60
|
}
|
|
62
61
|
```
|
|
@@ -117,6 +116,15 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
117
116
|
<sub><b>Flo</b></sub>
|
|
118
117
|
</a>
|
|
119
118
|
</td>
|
|
119
|
+
<td align="center">
|
|
120
|
+
<a href="https://github.com/francescolf">
|
|
121
|
+
<img src="https://avatars.githubusercontent.com/u/14892143?v=4" width="100;" alt="francescolf"/>
|
|
122
|
+
<br />
|
|
123
|
+
<sub><b>Francesco Lo Faro</b></sub>
|
|
124
|
+
</a>
|
|
125
|
+
</td>
|
|
126
|
+
</tr>
|
|
127
|
+
<tr>
|
|
120
128
|
<td align="center">
|
|
121
129
|
<a href="https://github.com/lchavezcuu">
|
|
122
130
|
<img src="https://avatars.githubusercontent.com/u/22165856?v=4" width="100;" alt="lchavezcuu"/>
|
|
@@ -124,8 +132,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
124
132
|
<sub><b>Luis Chavez</b></sub>
|
|
125
133
|
</a>
|
|
126
134
|
</td>
|
|
127
|
-
</tr>
|
|
128
|
-
<tr>
|
|
129
135
|
<td align="center">
|
|
130
136
|
<a href="https://github.com/maxmati">
|
|
131
137
|
<img src="https://avatars.githubusercontent.com/u/509560?v=4" width="100;" alt="maxmati"/>
|
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import base64
|
|
5
5
|
import hashlib
|
|
6
|
-
import mimetypes
|
|
7
6
|
import secrets
|
|
8
7
|
import uuid
|
|
9
|
-
from
|
|
8
|
+
from collections.abc import Callable, Coroutine
|
|
10
9
|
from datetime import UTC, datetime, timedelta
|
|
11
|
-
from enum import StrEnum
|
|
12
10
|
from http import HTTPMethod, HTTPStatus
|
|
13
11
|
from http.cookies import Morsel
|
|
14
|
-
from pathlib import Path
|
|
15
12
|
from typing import Any, cast
|
|
16
13
|
from urllib.parse import parse_qs, urlencode
|
|
17
14
|
|
|
@@ -30,9 +27,12 @@ from multidict import MultiDictProxy
|
|
|
30
27
|
from yarl import URL
|
|
31
28
|
|
|
32
29
|
from . import __version__
|
|
33
|
-
from .const import (
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
from .const.devices import (
|
|
31
|
+
DEVICE_TO_IGNORE,
|
|
32
|
+
DEVICE_TYPE_TO_MODEL,
|
|
33
|
+
SPEAKER_GROUP_FAMILY,
|
|
34
|
+
)
|
|
35
|
+
from .const.http import (
|
|
36
36
|
AMAZON_APP_BUNDLE_ID,
|
|
37
37
|
AMAZON_APP_ID,
|
|
38
38
|
AMAZON_APP_NAME,
|
|
@@ -41,34 +41,30 @@ from .const import (
|
|
|
41
41
|
AMAZON_DEVICE_SOFTWARE_VERSION,
|
|
42
42
|
AMAZON_DEVICE_TYPE,
|
|
43
43
|
ARRAY_WRAPPER,
|
|
44
|
-
BIN_EXTENSION,
|
|
45
|
-
COUNTRY_GROUPS,
|
|
46
44
|
CSRF_COOKIE,
|
|
47
45
|
DEFAULT_HEADERS,
|
|
48
46
|
DEFAULT_SITE,
|
|
49
|
-
DEVICE_TO_IGNORE,
|
|
50
|
-
DEVICE_TYPE_TO_MODEL,
|
|
51
|
-
HTML_EXTENSION,
|
|
52
47
|
HTTP_ERROR_199,
|
|
53
48
|
HTTP_ERROR_299,
|
|
54
|
-
JSON_EXTENSION,
|
|
55
|
-
NOTIFICATION_ALARM,
|
|
56
|
-
NOTIFICATION_MUSIC_ALARM,
|
|
57
|
-
NOTIFICATION_REMINDER,
|
|
58
|
-
NOTIFICATION_TIMER,
|
|
59
|
-
NOTIFICATIONS_SUPPORTED,
|
|
60
|
-
RECURRING_PATTERNS,
|
|
61
49
|
REFRESH_ACCESS_TOKEN,
|
|
62
50
|
REFRESH_AUTH_COOKIES,
|
|
63
51
|
REQUEST_AGENT,
|
|
64
|
-
SAVE_PATH,
|
|
65
|
-
SENSORS,
|
|
66
|
-
SPEAKER_GROUP_FAMILY,
|
|
67
52
|
URI_DEVICES,
|
|
68
53
|
URI_DND,
|
|
69
54
|
URI_NEXUS_GRAPHQL,
|
|
70
55
|
URI_NOTIFICATIONS,
|
|
71
56
|
URI_SIGNIN,
|
|
57
|
+
)
|
|
58
|
+
from .const.metadata import ALEXA_INFO_SKILLS, SENSORS
|
|
59
|
+
from .const.queries import QUERY_DEVICE_DATA, QUERY_SENSOR_STATE
|
|
60
|
+
from .const.schedules import (
|
|
61
|
+
COUNTRY_GROUPS,
|
|
62
|
+
NOTIFICATION_ALARM,
|
|
63
|
+
NOTIFICATION_MUSIC_ALARM,
|
|
64
|
+
NOTIFICATION_REMINDER,
|
|
65
|
+
NOTIFICATION_TIMER,
|
|
66
|
+
NOTIFICATIONS_SUPPORTED,
|
|
67
|
+
RECURRING_PATTERNS,
|
|
72
68
|
WEEKEND_EXCEPTIONS,
|
|
73
69
|
)
|
|
74
70
|
from .exceptions import (
|
|
@@ -78,68 +74,14 @@ from .exceptions import (
|
|
|
78
74
|
CannotRetrieveData,
|
|
79
75
|
WrongMethod,
|
|
80
76
|
)
|
|
81
|
-
from .
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
name: str
|
|
90
|
-
value: str | int | float
|
|
91
|
-
error: bool
|
|
92
|
-
error_type: str | None
|
|
93
|
-
error_msg: str | None
|
|
94
|
-
scale: str | None
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
@dataclass
|
|
98
|
-
class AmazonSchedule:
|
|
99
|
-
"""Amazon schedule class."""
|
|
100
|
-
|
|
101
|
-
type: str # alarm, reminder, timer
|
|
102
|
-
status: str
|
|
103
|
-
label: str
|
|
104
|
-
next_occurrence: datetime | None
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
@dataclass
|
|
108
|
-
class AmazonDevice:
|
|
109
|
-
"""Amazon device class."""
|
|
110
|
-
|
|
111
|
-
account_name: str
|
|
112
|
-
capabilities: list[str]
|
|
113
|
-
device_family: str
|
|
114
|
-
device_type: str
|
|
115
|
-
device_owner_customer_id: str
|
|
116
|
-
household_device: bool
|
|
117
|
-
device_cluster_members: list[str]
|
|
118
|
-
online: bool
|
|
119
|
-
serial_number: str
|
|
120
|
-
software_version: str
|
|
121
|
-
entity_id: str | None
|
|
122
|
-
endpoint_id: str | None
|
|
123
|
-
sensors: dict[str, AmazonDeviceSensor]
|
|
124
|
-
notifications: dict[str, AmazonSchedule]
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class AmazonSequenceType(StrEnum):
|
|
128
|
-
"""Amazon sequence types."""
|
|
129
|
-
|
|
130
|
-
Announcement = "AlexaAnnouncement"
|
|
131
|
-
Speak = "Alexa.Speak"
|
|
132
|
-
Sound = "Alexa.Sound"
|
|
133
|
-
Music = "Alexa.Music.PlaySearchPhrase"
|
|
134
|
-
TextCommand = "Alexa.TextCommand"
|
|
135
|
-
LaunchSkill = "Alexa.Operation.SkillConnections.Launch"
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
class AmazonMusicSource(StrEnum):
|
|
139
|
-
"""Amazon music sources."""
|
|
140
|
-
|
|
141
|
-
Radio = "TUNEIN"
|
|
142
|
-
AmazonMusic = "AMAZON_MUSIC"
|
|
77
|
+
from .structures import (
|
|
78
|
+
AmazonDevice,
|
|
79
|
+
AmazonDeviceSensor,
|
|
80
|
+
AmazonMusicSource,
|
|
81
|
+
AmazonSchedule,
|
|
82
|
+
AmazonSequenceType,
|
|
83
|
+
)
|
|
84
|
+
from .utils import _LOGGER, obfuscate_email, scrub_fields
|
|
143
85
|
|
|
144
86
|
|
|
145
87
|
class AmazonEchoApi:
|
|
@@ -151,6 +93,8 @@ class AmazonEchoApi:
|
|
|
151
93
|
login_email: str,
|
|
152
94
|
login_password: str,
|
|
153
95
|
login_data: dict[str, Any] | None = None,
|
|
96
|
+
save_to_file: Callable[[str | dict, str, str], Coroutine[Any, Any, None]]
|
|
97
|
+
| None = None,
|
|
154
98
|
) -> None:
|
|
155
99
|
"""Initialize the scanner."""
|
|
156
100
|
# Check if there is a previous login, otherwise use default (US)
|
|
@@ -162,7 +106,7 @@ class AmazonEchoApi:
|
|
|
162
106
|
self._login_password = login_password
|
|
163
107
|
|
|
164
108
|
self._cookies = self._build_init_cookies()
|
|
165
|
-
self.
|
|
109
|
+
self._save_to_file = save_to_file
|
|
166
110
|
self._login_stored_data = login_data or {}
|
|
167
111
|
self._serial = self._serial_number()
|
|
168
112
|
self._account_owner_customer_id: str | None = None
|
|
@@ -183,11 +127,6 @@ class AmazonEchoApi:
|
|
|
183
127
|
"""Return current Amazon domain."""
|
|
184
128
|
return self._domain
|
|
185
129
|
|
|
186
|
-
def save_raw_data(self) -> None:
|
|
187
|
-
"""Save raw data to disk."""
|
|
188
|
-
self._save_raw_data = True
|
|
189
|
-
_LOGGER.debug("Saving raw data to disk")
|
|
190
|
-
|
|
191
130
|
def _country_specific_data(self, domain: str) -> None:
|
|
192
131
|
"""Set country specific data."""
|
|
193
132
|
# Force lower case
|
|
@@ -387,6 +326,8 @@ class AmazonEchoApi:
|
|
|
387
326
|
headers = DEFAULT_HEADERS.copy()
|
|
388
327
|
headers.update({"User-Agent": REQUEST_AGENT[agent]})
|
|
389
328
|
headers.update({"Accept-Language": self._language})
|
|
329
|
+
headers.update({"x-amzn-client": "aioamazondevices"})
|
|
330
|
+
headers.update({"x-amzn-build-version": __version__})
|
|
390
331
|
|
|
391
332
|
if self._csrf_cookie:
|
|
392
333
|
csrf = {CSRF_COOKIE: self._csrf_cookie}
|
|
@@ -463,57 +404,16 @@ class AmazonEchoApi:
|
|
|
463
404
|
f"Request failed: {await self._http_phrase_error(resp.status)}"
|
|
464
405
|
)
|
|
465
406
|
|
|
466
|
-
await
|
|
467
|
-
await resp.text(),
|
|
468
|
-
url,
|
|
469
|
-
mimetypes.guess_extension(content_type.split(";")[0]) or ".raw",
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
return BeautifulSoup(await resp.read() or "", "html.parser"), resp
|
|
473
|
-
|
|
474
|
-
async def _save_to_file(
|
|
475
|
-
self,
|
|
476
|
-
raw_data: str | dict,
|
|
477
|
-
url: str,
|
|
478
|
-
extension: str = HTML_EXTENSION,
|
|
479
|
-
output_path: str = SAVE_PATH,
|
|
480
|
-
) -> None:
|
|
481
|
-
"""Save response data to disk."""
|
|
482
|
-
if not self._save_raw_data or not raw_data:
|
|
483
|
-
return
|
|
484
|
-
|
|
485
|
-
output_dir = Path(output_path)
|
|
486
|
-
output_dir.mkdir(parents=True, exist_ok=True)
|
|
487
|
-
|
|
488
|
-
if url.startswith("http"):
|
|
489
|
-
url_split = url.split("/")
|
|
490
|
-
base_filename = f"{url_split[3]}-{url_split[4].split('?')[0]}"
|
|
491
|
-
else:
|
|
492
|
-
base_filename = url
|
|
493
|
-
fullpath = Path(output_dir, base_filename + extension)
|
|
494
|
-
|
|
495
|
-
data: str
|
|
496
|
-
if isinstance(raw_data, dict):
|
|
497
|
-
data = orjson.dumps(raw_data, option=orjson.OPT_INDENT_2).decode("utf-8")
|
|
498
|
-
elif extension in [HTML_EXTENSION, BIN_EXTENSION]:
|
|
499
|
-
data = raw_data
|
|
500
|
-
else:
|
|
501
|
-
data = orjson.dumps(
|
|
502
|
-
orjson.loads(raw_data),
|
|
503
|
-
option=orjson.OPT_INDENT_2,
|
|
504
|
-
).decode("utf-8")
|
|
505
|
-
|
|
506
|
-
i = 2
|
|
507
|
-
while fullpath.exists():
|
|
508
|
-
filename = f"{base_filename}_{i!s}{extension}"
|
|
509
|
-
fullpath = Path(output_dir, filename)
|
|
510
|
-
i += 1
|
|
407
|
+
raw_content = await resp.read()
|
|
511
408
|
|
|
512
|
-
|
|
409
|
+
if self._save_to_file:
|
|
410
|
+
await self._save_to_file(
|
|
411
|
+
raw_content.decode("utf-8"),
|
|
412
|
+
url,
|
|
413
|
+
content_type,
|
|
414
|
+
)
|
|
513
415
|
|
|
514
|
-
|
|
515
|
-
file.write(data)
|
|
516
|
-
file.write("\n")
|
|
416
|
+
return BeautifulSoup(raw_content or "", "html.parser"), resp
|
|
517
417
|
|
|
518
418
|
async def _register_device(
|
|
519
419
|
self,
|
|
@@ -613,6 +513,9 @@ class AmazonEchoApi:
|
|
|
613
513
|
"""Retrieve devices sensors states."""
|
|
614
514
|
devices_sensors: dict[str, dict[str, AmazonDeviceSensor]] = {}
|
|
615
515
|
|
|
516
|
+
if not self._endpoints:
|
|
517
|
+
return {}
|
|
518
|
+
|
|
616
519
|
endpoint_ids = list(self._endpoints.keys())
|
|
617
520
|
payload = [
|
|
618
521
|
{
|
|
@@ -972,7 +875,6 @@ class AmazonEchoApi:
|
|
|
972
875
|
await self._domain_refresh_auth_cookies()
|
|
973
876
|
|
|
974
877
|
self._login_stored_data.update({"site": f"https://www.amazon.{self._domain}"})
|
|
975
|
-
await self._save_to_file(self._login_stored_data, "login_data", JSON_EXTENSION)
|
|
976
878
|
|
|
977
879
|
# Can take a little while to register device but we need it
|
|
978
880
|
# to be able to pickout account customer ID
|
|
@@ -1048,10 +950,6 @@ class AmazonEchoApi:
|
|
|
1048
950
|
obfuscate_email(self._login_email),
|
|
1049
951
|
)
|
|
1050
952
|
|
|
1051
|
-
# Check if session is still authenticated
|
|
1052
|
-
if not await self.auth_check_status():
|
|
1053
|
-
raise CannotAuthenticate("Session no longer authenticated")
|
|
1054
|
-
|
|
1055
953
|
return self._login_stored_data
|
|
1056
954
|
|
|
1057
955
|
async def _get_alexa_domain(self) -> str:
|
|
@@ -1274,29 +1172,6 @@ class AmazonEchoApi:
|
|
|
1274
1172
|
|
|
1275
1173
|
self._final_devices = final_devices_list
|
|
1276
1174
|
|
|
1277
|
-
async def auth_check_status(self) -> bool:
|
|
1278
|
-
"""Check AUTH status."""
|
|
1279
|
-
_, raw_resp = await self._session_request(
|
|
1280
|
-
method=HTTPMethod.GET,
|
|
1281
|
-
url=f"https://alexa.amazon.{self._domain}/api/bootstrap?version=0",
|
|
1282
|
-
agent="Browser",
|
|
1283
|
-
)
|
|
1284
|
-
if raw_resp.status != HTTPStatus.OK:
|
|
1285
|
-
_LOGGER.debug(
|
|
1286
|
-
"Session not authenticated: reply error %s",
|
|
1287
|
-
raw_resp.status,
|
|
1288
|
-
)
|
|
1289
|
-
return False
|
|
1290
|
-
|
|
1291
|
-
resp_json = await self._response_to_json(raw_resp)
|
|
1292
|
-
if not (authentication := resp_json.get("authentication")):
|
|
1293
|
-
_LOGGER.debug('Session not authenticated: reply missing "authentication"')
|
|
1294
|
-
return False
|
|
1295
|
-
|
|
1296
|
-
authenticated = authentication.get("authenticated")
|
|
1297
|
-
_LOGGER.debug("Session authenticated: %s", authenticated)
|
|
1298
|
-
return bool(authenticated)
|
|
1299
|
-
|
|
1300
1175
|
def get_model_details(self, device: AmazonDevice) -> dict[str, str | None] | None:
|
|
1301
1176
|
"""Return model datails."""
|
|
1302
1177
|
model_details: dict[str, str | None] | None = DEVICE_TYPE_TO_MODEL.get(
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""aioamazondevices const package."""
|
|
@@ -1,117 +1,10 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""aioamazondevices devices."""
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
_LOGGER = logging.getLogger(__package__)
|
|
6
|
-
|
|
7
|
-
ARRAY_WRAPPER = "generatedArrayWrapper"
|
|
8
|
-
|
|
9
|
-
HTTP_ERROR_199 = 199
|
|
10
|
-
HTTP_ERROR_299 = 299
|
|
11
|
-
|
|
12
|
-
TO_REDACT = {
|
|
13
|
-
"address",
|
|
14
|
-
"address1",
|
|
15
|
-
"address2",
|
|
16
|
-
"address3",
|
|
17
|
-
"city",
|
|
18
|
-
"county",
|
|
19
|
-
"customerId",
|
|
20
|
-
"deviceAccountId",
|
|
21
|
-
"deviceAddress",
|
|
22
|
-
"deviceOwnerCustomerId",
|
|
23
|
-
"given_name",
|
|
24
|
-
"name",
|
|
25
|
-
"password",
|
|
26
|
-
"postalCode",
|
|
27
|
-
"searchCustomerId",
|
|
28
|
-
"state",
|
|
29
|
-
"street",
|
|
30
|
-
"user_id",
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# Amazon APP info
|
|
34
|
-
AMAZON_APP_BUNDLE_ID = "com.amazon.echo"
|
|
35
|
-
AMAZON_APP_ID = "MAPiOSLib/6.0/ToHideRetailLink"
|
|
36
|
-
AMAZON_APP_NAME = "AioAmazonDevices"
|
|
37
|
-
AMAZON_APP_VERSION = "2.2.663733.0"
|
|
38
|
-
AMAZON_DEVICE_SOFTWARE_VERSION = "35602678"
|
|
39
|
-
AMAZON_DEVICE_TYPE = "A2IVLV5VM2W81"
|
|
40
|
-
AMAZON_CLIENT_OS = "18.5"
|
|
41
|
-
|
|
42
|
-
DEFAULT_SITE = "https://www.amazon.com"
|
|
43
|
-
DEFAULT_HEADERS = {
|
|
44
|
-
"Accept-Charset": "utf-8",
|
|
45
|
-
"Accept-Encoding": "gzip",
|
|
46
|
-
"Connection": "keep-alive",
|
|
47
|
-
}
|
|
48
|
-
CSRF_COOKIE = "csrf"
|
|
49
|
-
REQUEST_AGENT = {
|
|
50
|
-
"Amazon": f"AmazonWebView/AmazonAlexa/{AMAZON_APP_VERSION}/iOS/{AMAZON_CLIENT_OS}/iPhone", # noqa: E501
|
|
51
|
-
"Browser": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0", # noqa: E501
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
REFRESH_ACCESS_TOKEN = "access_token" # noqa: S105
|
|
55
|
-
REFRESH_AUTH_COOKIES = "auth_cookies"
|
|
56
|
-
|
|
57
|
-
URI_DEVICES = "/api/devices-v2/device"
|
|
58
|
-
URI_DND = "/api/dnd/device-status-list"
|
|
59
|
-
URI_NOTIFICATIONS = "/api/notifications"
|
|
60
|
-
URI_SIGNIN = "/ap/signin"
|
|
61
|
-
URI_NEXUS_GRAPHQL = "/nexus/v1/graphql"
|
|
62
|
-
|
|
63
|
-
SENSOR_STATE_OFF = "NOT_DETECTED"
|
|
64
|
-
|
|
65
|
-
# File extensions
|
|
66
|
-
SAVE_PATH = "out"
|
|
67
|
-
HTML_EXTENSION = ".html"
|
|
68
|
-
JSON_EXTENSION = ".json"
|
|
69
|
-
BIN_EXTENSION = ".bin"
|
|
3
|
+
from .http import AMAZON_DEVICE_TYPE
|
|
70
4
|
|
|
71
5
|
SPEAKER_GROUP_FAMILY = "WHA"
|
|
72
6
|
SPEAKER_GROUP_MODEL = "Speaker Group"
|
|
73
7
|
|
|
74
|
-
SENSORS: dict[str, dict[str, str | None]] = {
|
|
75
|
-
"temperatureSensor": {
|
|
76
|
-
"name": "temperature",
|
|
77
|
-
"key": "value",
|
|
78
|
-
"subkey": "value",
|
|
79
|
-
"scale": "scale",
|
|
80
|
-
},
|
|
81
|
-
"motionSensor": {
|
|
82
|
-
"name": "detectionState",
|
|
83
|
-
"key": "detectionStateValue",
|
|
84
|
-
"subkey": None,
|
|
85
|
-
"scale": None,
|
|
86
|
-
},
|
|
87
|
-
"lightSensor": {
|
|
88
|
-
"name": "illuminance",
|
|
89
|
-
"key": "illuminanceValue",
|
|
90
|
-
"subkey": "value",
|
|
91
|
-
"scale": None,
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
ALEXA_INFO_SKILLS = [
|
|
96
|
-
"Alexa.Calendar.PlayToday",
|
|
97
|
-
"Alexa.Calendar.PlayTomorrow",
|
|
98
|
-
"Alexa.Calendar.PlayNext",
|
|
99
|
-
"Alexa.Date.Play",
|
|
100
|
-
"Alexa.Time.Play",
|
|
101
|
-
"Alexa.News.NationalNews",
|
|
102
|
-
"Alexa.FlashBriefing.Play",
|
|
103
|
-
"Alexa.Traffic.Play",
|
|
104
|
-
"Alexa.Weather.Play",
|
|
105
|
-
"Alexa.CleanUp.Play",
|
|
106
|
-
"Alexa.GoodMorning.Play",
|
|
107
|
-
"Alexa.SingASong.Play",
|
|
108
|
-
"Alexa.FunFact.Play",
|
|
109
|
-
"Alexa.Joke.Play",
|
|
110
|
-
"Alexa.TellStory.Play",
|
|
111
|
-
"Alexa.ImHome.Play",
|
|
112
|
-
"Alexa.GoodNight.Play",
|
|
113
|
-
]
|
|
114
|
-
|
|
115
8
|
DEVICE_TO_IGNORE: list[str] = [
|
|
116
9
|
AMAZON_DEVICE_TYPE, # Alexa App for iOS
|
|
117
10
|
"A2TF17PFR55MTB", # Alexa App for Android
|
|
@@ -480,63 +373,3 @@ DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
|
|
|
480
373
|
"hw_version": "Gen2",
|
|
481
374
|
},
|
|
482
375
|
}
|
|
483
|
-
|
|
484
|
-
RECURRING_PATTERNS: dict[str, str] = {
|
|
485
|
-
"XXXX-WD": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
|
|
486
|
-
"XXXX-WE": "FREQ=WEEKLY;BYDAY=SA,SU",
|
|
487
|
-
"XXXX-WXX-1": "FREQ=WEEKLY;BYDAY=MO",
|
|
488
|
-
"XXXX-WXX-2": "FREQ=WEEKLY;BYDAY=TU",
|
|
489
|
-
"XXXX-WXX-3": "FREQ=WEEKLY;BYDAY=WE",
|
|
490
|
-
"XXXX-WXX-4": "FREQ=WEEKLY;BYDAY=TH",
|
|
491
|
-
"XXXX-WXX-5": "FREQ=WEEKLY;BYDAY=FR",
|
|
492
|
-
"XXXX-WXX-6": "FREQ=WEEKLY;BYDAY=SA",
|
|
493
|
-
"XXXX-WXX-7": "FREQ=WEEKLY;BYDAY=SU",
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
WEEKEND_EXCEPTIONS = {
|
|
497
|
-
"TH-FR": {
|
|
498
|
-
"XXXX-WD": "FREQ=WEEKLY;BYDAY=MO,TU,WE,SA,SU",
|
|
499
|
-
"XXXX-WE": "FREQ=WEEKLY;BYDAY=TH,FR",
|
|
500
|
-
},
|
|
501
|
-
"FR-SA": {
|
|
502
|
-
"XXXX-WD": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,SU",
|
|
503
|
-
"XXXX-WE": "FREQ=WEEKLY;BYDAY=FR,SA",
|
|
504
|
-
},
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
# Countries grouped by their weekend type
|
|
508
|
-
COUNTRY_GROUPS = {
|
|
509
|
-
"TH-FR": ["IR"],
|
|
510
|
-
"FR-SA": [
|
|
511
|
-
"AF",
|
|
512
|
-
"BD",
|
|
513
|
-
"BH",
|
|
514
|
-
"DZ",
|
|
515
|
-
"EG",
|
|
516
|
-
"IL",
|
|
517
|
-
"IQ",
|
|
518
|
-
"JO",
|
|
519
|
-
"KW",
|
|
520
|
-
"LY",
|
|
521
|
-
"MV",
|
|
522
|
-
"MY",
|
|
523
|
-
"OM",
|
|
524
|
-
"PS",
|
|
525
|
-
"QA",
|
|
526
|
-
"SA",
|
|
527
|
-
"SD",
|
|
528
|
-
"SY",
|
|
529
|
-
"YE",
|
|
530
|
-
],
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
NOTIFICATION_ALARM = "Alarm"
|
|
534
|
-
NOTIFICATION_MUSIC_ALARM = "MusicAlarm"
|
|
535
|
-
NOTIFICATION_REMINDER = "Reminder"
|
|
536
|
-
NOTIFICATION_TIMER = "Timer"
|
|
537
|
-
NOTIFICATIONS_SUPPORTED = [
|
|
538
|
-
NOTIFICATION_ALARM,
|
|
539
|
-
NOTIFICATION_MUSIC_ALARM,
|
|
540
|
-
NOTIFICATION_REMINDER,
|
|
541
|
-
NOTIFICATION_TIMER,
|
|
542
|
-
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""aioamazondevices HTTP const."""
|
|
2
|
+
|
|
3
|
+
HTTP_ERROR_199 = 199
|
|
4
|
+
HTTP_ERROR_299 = 299
|
|
5
|
+
|
|
6
|
+
ARRAY_WRAPPER = "generatedArrayWrapper"
|
|
7
|
+
|
|
8
|
+
# Amazon APP info
|
|
9
|
+
AMAZON_APP_BUNDLE_ID = "com.amazon.echo"
|
|
10
|
+
AMAZON_APP_ID = "MAPiOSLib/6.0/ToHideRetailLink"
|
|
11
|
+
AMAZON_APP_NAME = "AioAmazonDevices"
|
|
12
|
+
AMAZON_APP_VERSION = "2.2.663733.0"
|
|
13
|
+
AMAZON_DEVICE_SOFTWARE_VERSION = "35602678"
|
|
14
|
+
AMAZON_DEVICE_TYPE = "A2IVLV5VM2W81"
|
|
15
|
+
AMAZON_CLIENT_OS = "18.5"
|
|
16
|
+
|
|
17
|
+
DEFAULT_SITE = "https://www.amazon.com"
|
|
18
|
+
DEFAULT_HEADERS = {
|
|
19
|
+
"Accept-Charset": "utf-8",
|
|
20
|
+
"Accept-Encoding": "gzip",
|
|
21
|
+
"Connection": "keep-alive",
|
|
22
|
+
}
|
|
23
|
+
CSRF_COOKIE = "csrf"
|
|
24
|
+
REQUEST_AGENT = {
|
|
25
|
+
"Amazon": f"AmazonWebView/AmazonAlexa/{AMAZON_APP_VERSION}/iOS/{AMAZON_CLIENT_OS}/iPhone", # noqa: E501
|
|
26
|
+
"Browser": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36 Edg/141.0.0.0", # noqa: E501
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
REFRESH_ACCESS_TOKEN = "access_token" # noqa: S105
|
|
30
|
+
REFRESH_AUTH_COOKIES = "auth_cookies"
|
|
31
|
+
|
|
32
|
+
URI_DEVICES = "/api/devices-v2/device"
|
|
33
|
+
URI_DND = "/api/dnd/device-status-list"
|
|
34
|
+
URI_NOTIFICATIONS = "/api/notifications"
|
|
35
|
+
URI_SIGNIN = "/ap/signin"
|
|
36
|
+
URI_NEXUS_GRAPHQL = "/nexus/v1/graphql"
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""aioamazondevices Additional entities const."""
|
|
2
|
+
|
|
3
|
+
SENSOR_STATE_OFF = "NOT_DETECTED"
|
|
4
|
+
|
|
5
|
+
SENSORS: dict[str, dict[str, str | None]] = {
|
|
6
|
+
"temperatureSensor": {
|
|
7
|
+
"name": "temperature",
|
|
8
|
+
"key": "value",
|
|
9
|
+
"subkey": "value",
|
|
10
|
+
"scale": "scale",
|
|
11
|
+
},
|
|
12
|
+
"motionSensor": {
|
|
13
|
+
"name": "detectionState",
|
|
14
|
+
"key": "detectionStateValue",
|
|
15
|
+
"subkey": None,
|
|
16
|
+
"scale": None,
|
|
17
|
+
},
|
|
18
|
+
"lightSensor": {
|
|
19
|
+
"name": "illuminance",
|
|
20
|
+
"key": "illuminanceValue",
|
|
21
|
+
"subkey": "value",
|
|
22
|
+
"scale": None,
|
|
23
|
+
},
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
ALEXA_INFO_SKILLS = [
|
|
27
|
+
"Alexa.Calendar.PlayToday",
|
|
28
|
+
"Alexa.Calendar.PlayTomorrow",
|
|
29
|
+
"Alexa.Calendar.PlayNext",
|
|
30
|
+
"Alexa.Date.Play",
|
|
31
|
+
"Alexa.Time.Play",
|
|
32
|
+
"Alexa.News.NationalNews",
|
|
33
|
+
"Alexa.FlashBriefing.Play",
|
|
34
|
+
"Alexa.Traffic.Play",
|
|
35
|
+
"Alexa.Weather.Play",
|
|
36
|
+
"Alexa.CleanUp.Play",
|
|
37
|
+
"Alexa.GoodMorning.Play",
|
|
38
|
+
"Alexa.SingASong.Play",
|
|
39
|
+
"Alexa.FunFact.Play",
|
|
40
|
+
"Alexa.Joke.Play",
|
|
41
|
+
"Alexa.TellStory.Play",
|
|
42
|
+
"Alexa.ImHome.Play",
|
|
43
|
+
"Alexa.GoodNight.Play",
|
|
44
|
+
]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""aioamazondevices: schedules."""
|
|
2
|
+
|
|
3
|
+
NOTIFICATION_ALARM = "Alarm"
|
|
4
|
+
NOTIFICATION_MUSIC_ALARM = "MusicAlarm"
|
|
5
|
+
NOTIFICATION_REMINDER = "Reminder"
|
|
6
|
+
NOTIFICATION_TIMER = "Timer"
|
|
7
|
+
NOTIFICATIONS_SUPPORTED = [
|
|
8
|
+
NOTIFICATION_ALARM,
|
|
9
|
+
NOTIFICATION_MUSIC_ALARM,
|
|
10
|
+
NOTIFICATION_REMINDER,
|
|
11
|
+
NOTIFICATION_TIMER,
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
RECURRING_PATTERNS: dict[str, str] = {
|
|
15
|
+
"XXXX-WD": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR",
|
|
16
|
+
"XXXX-WE": "FREQ=WEEKLY;BYDAY=SA,SU",
|
|
17
|
+
"XXXX-WXX-1": "FREQ=WEEKLY;BYDAY=MO",
|
|
18
|
+
"XXXX-WXX-2": "FREQ=WEEKLY;BYDAY=TU",
|
|
19
|
+
"XXXX-WXX-3": "FREQ=WEEKLY;BYDAY=WE",
|
|
20
|
+
"XXXX-WXX-4": "FREQ=WEEKLY;BYDAY=TH",
|
|
21
|
+
"XXXX-WXX-5": "FREQ=WEEKLY;BYDAY=FR",
|
|
22
|
+
"XXXX-WXX-6": "FREQ=WEEKLY;BYDAY=SA",
|
|
23
|
+
"XXXX-WXX-7": "FREQ=WEEKLY;BYDAY=SU",
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
WEEKEND_EXCEPTIONS = {
|
|
27
|
+
"TH-FR": {
|
|
28
|
+
"XXXX-WD": "FREQ=WEEKLY;BYDAY=MO,TU,WE,SA,SU",
|
|
29
|
+
"XXXX-WE": "FREQ=WEEKLY;BYDAY=TH,FR",
|
|
30
|
+
},
|
|
31
|
+
"FR-SA": {
|
|
32
|
+
"XXXX-WD": "FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,SU",
|
|
33
|
+
"XXXX-WE": "FREQ=WEEKLY;BYDAY=FR,SA",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Countries grouped by their weekend type
|
|
38
|
+
COUNTRY_GROUPS = {
|
|
39
|
+
"TH-FR": ["IR"],
|
|
40
|
+
"FR-SA": [
|
|
41
|
+
"AF",
|
|
42
|
+
"BD",
|
|
43
|
+
"BH",
|
|
44
|
+
"DZ",
|
|
45
|
+
"EG",
|
|
46
|
+
"IL",
|
|
47
|
+
"IQ",
|
|
48
|
+
"JO",
|
|
49
|
+
"KW",
|
|
50
|
+
"LY",
|
|
51
|
+
"MV",
|
|
52
|
+
"MY",
|
|
53
|
+
"OM",
|
|
54
|
+
"PS",
|
|
55
|
+
"QA",
|
|
56
|
+
"SA",
|
|
57
|
+
"SD",
|
|
58
|
+
"SY",
|
|
59
|
+
"YE",
|
|
60
|
+
],
|
|
61
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"""aioamazondevices structures module."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from enum import StrEnum
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class AmazonDeviceSensor:
|
|
10
|
+
"""Amazon device sensor class."""
|
|
11
|
+
|
|
12
|
+
name: str
|
|
13
|
+
value: str | int | float
|
|
14
|
+
error: bool
|
|
15
|
+
error_type: str | None
|
|
16
|
+
error_msg: str | None
|
|
17
|
+
scale: str | None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class AmazonSchedule:
|
|
22
|
+
"""Amazon schedule class."""
|
|
23
|
+
|
|
24
|
+
type: str # alarm, reminder, timer
|
|
25
|
+
status: str
|
|
26
|
+
label: str
|
|
27
|
+
next_occurrence: datetime | None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class AmazonDevice:
|
|
32
|
+
"""Amazon device class."""
|
|
33
|
+
|
|
34
|
+
account_name: str
|
|
35
|
+
capabilities: list[str]
|
|
36
|
+
device_family: str
|
|
37
|
+
device_type: str
|
|
38
|
+
device_owner_customer_id: str
|
|
39
|
+
household_device: bool
|
|
40
|
+
device_cluster_members: list[str]
|
|
41
|
+
online: bool
|
|
42
|
+
serial_number: str
|
|
43
|
+
software_version: str
|
|
44
|
+
entity_id: str | None
|
|
45
|
+
endpoint_id: str | None
|
|
46
|
+
sensors: dict[str, AmazonDeviceSensor]
|
|
47
|
+
notifications: dict[str, AmazonSchedule]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class AmazonSequenceType(StrEnum):
|
|
51
|
+
"""Amazon sequence types."""
|
|
52
|
+
|
|
53
|
+
Announcement = "AlexaAnnouncement"
|
|
54
|
+
Speak = "Alexa.Speak"
|
|
55
|
+
Sound = "Alexa.Sound"
|
|
56
|
+
Music = "Alexa.Music.PlaySearchPhrase"
|
|
57
|
+
TextCommand = "Alexa.TextCommand"
|
|
58
|
+
LaunchSkill = "Alexa.Operation.SkillConnections.Launch"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AmazonMusicSource(StrEnum):
|
|
62
|
+
"""Amazon music sources."""
|
|
63
|
+
|
|
64
|
+
Radio = "TUNEIN"
|
|
65
|
+
AmazonMusic = "AMAZON_MUSIC"
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
"""Utils module for Amazon devices."""
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
from collections.abc import Collection
|
|
4
5
|
from typing import Any
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
_LOGGER = logging.getLogger(__package__)
|
|
8
|
+
|
|
9
|
+
TO_REDACT = {
|
|
10
|
+
"address",
|
|
11
|
+
"address1",
|
|
12
|
+
"address2",
|
|
13
|
+
"address3",
|
|
14
|
+
"city",
|
|
15
|
+
"county",
|
|
16
|
+
"customerId",
|
|
17
|
+
"deviceAccountId",
|
|
18
|
+
"deviceAddress",
|
|
19
|
+
"deviceOwnerCustomerId",
|
|
20
|
+
"given_name",
|
|
21
|
+
"name",
|
|
22
|
+
"password",
|
|
23
|
+
"postalCode",
|
|
24
|
+
"searchCustomerId",
|
|
25
|
+
"state",
|
|
26
|
+
"street",
|
|
27
|
+
"user_id",
|
|
28
|
+
}
|
|
7
29
|
|
|
8
30
|
|
|
9
31
|
def obfuscate_email(email: str) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|