aioamazondevices 3.0.9__py3-none-any.whl → 3.1.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.
@@ -1,6 +1,6 @@
1
1
  """aioamazondevices library."""
2
2
 
3
- __version__ = "3.0.9"
3
+ __version__ = "3.1.0"
4
4
 
5
5
 
6
6
  from .api import AmazonDevice, AmazonEchoApi
aioamazondevices/api.py CHANGED
@@ -34,6 +34,7 @@ from .const import (
34
34
  CSRF_COOKIE,
35
35
  DEFAULT_ASSOC_HANDLE,
36
36
  DEFAULT_HEADERS,
37
+ DEVICE_TO_IGNORE,
37
38
  DEVICE_TYPE_TO_MODEL,
38
39
  DOMAIN_BY_ISO3166_COUNTRY,
39
40
  HTML_EXTENSION,
@@ -41,9 +42,13 @@ from .const import (
41
42
  NODE_BLUETOOTH,
42
43
  NODE_DEVICES,
43
44
  NODE_DO_NOT_DISTURB,
45
+ NODE_IDENTIFIER,
44
46
  NODE_PREFERENCES,
45
47
  SAVE_PATH,
48
+ SENSORS,
49
+ URI_IDS,
46
50
  URI_QUERIES,
51
+ URI_SENSORS,
47
52
  )
48
53
  from .exceptions import (
49
54
  CannotAuthenticate,
@@ -53,6 +58,15 @@ from .exceptions import (
53
58
  )
54
59
 
55
60
 
61
+ @dataclass
62
+ class AmazonDeviceSensor:
63
+ """Amazon device sensor class."""
64
+
65
+ name: str
66
+ value: Any
67
+ scale: str | None
68
+
69
+
56
70
  @dataclass
57
71
  class AmazonDevice:
58
72
  """Amazon device class."""
@@ -69,6 +83,9 @@ class AmazonDevice:
69
83
  do_not_disturb: bool
70
84
  response_style: str | None
71
85
  bluetooth_state: bool
86
+ entity_id: str
87
+ appliance_id: str
88
+ sensors: dict[str, AmazonDeviceSensor]
72
89
 
73
90
 
74
91
  class AmazonSequenceType(StrEnum):
@@ -124,6 +141,7 @@ class AmazonEchoApi:
124
141
  self._list_for_clusters: dict[str, str] = {}
125
142
 
126
143
  self.session: ClientSession
144
+ self._devices: dict[str, Any] = {}
127
145
 
128
146
  def _load_website_cookies(self) -> dict[str, str]:
129
147
  """Get website cookies, if avaliables."""
@@ -484,6 +502,84 @@ class AmazonEchoApi:
484
502
  await self._save_to_file(login_data, "login_data", JSON_EXTENSION)
485
503
  return login_data
486
504
 
505
+ async def _get_devices_ids(self) -> list[dict[str, str]]:
506
+ """Retrieve devices entityId and applianceId."""
507
+ _, raw_resp = await self._session_request(
508
+ "GET",
509
+ url=f"https://alexa.amazon.{self._domain}{URI_IDS}",
510
+ )
511
+ json_data = await raw_resp.json()
512
+
513
+ network_detail = orjson.loads(json_data["networkDetail"])
514
+ # Navigate through the nested structure step by step
515
+ location_details = network_detail["locationDetails"]["locationDetails"]
516
+ default_location = location_details["Default_Location"]
517
+ amazon_bridge = default_location["amazonBridgeDetails"]["amazonBridgeDetails"]
518
+ lambda_bridge = amazon_bridge["LambdaBridge_AAA/SonarCloudService"]
519
+ appliance_details = lambda_bridge["applianceDetails"]["applianceDetails"]
520
+
521
+ entity_ids_list: list[dict[str, str]] = []
522
+ # Process each appliance that starts with AAA_SonarCloudService
523
+ for appliance_key, appliance_data in appliance_details.items():
524
+ if not appliance_key.startswith("AAA_SonarCloudService"):
525
+ continue
526
+
527
+ entity_id = appliance_data["entityId"]
528
+ appliance_id = appliance_data["applianceId"]
529
+
530
+ # Create identifier object for this appliance
531
+ identifier = {
532
+ "entityId": entity_id,
533
+ "applianceId": appliance_id,
534
+ }
535
+
536
+ # Update device information for each device in the identifier list
537
+ for device_identifier in appliance_data["alexaDeviceIdentifierList"]:
538
+ serial_number = device_identifier["dmsDeviceSerialNumber"]
539
+
540
+ # Add identifier information to the device
541
+ self._devices[serial_number] |= {NODE_IDENTIFIER: identifier}
542
+
543
+ # Add to entity IDs list for sensor retrieval
544
+ entity_ids_list.append({"entityId": entity_id, "entityType": "ENTITY"})
545
+
546
+ return entity_ids_list
547
+
548
+ async def _get_sensors_states(
549
+ self, entity_ids_list: list[dict[str, str]]
550
+ ) -> dict[str, dict[str, AmazonDeviceSensor]]:
551
+ """Retrieve devices sensors states."""
552
+ _data = {"stateRequests": entity_ids_list}
553
+ _, raw_resp = await self._session_request(
554
+ "POST",
555
+ url=f"https://alexa.amazon.{self._domain}{URI_SENSORS}",
556
+ input_data=_data,
557
+ json_data=True,
558
+ )
559
+ json_data = await raw_resp.json()
560
+
561
+ final_sensors: dict[str, dict[str, AmazonDeviceSensor]] = {}
562
+ for sensors in json_data["deviceStates"]:
563
+ _id = sensors["entity"]["entityId"]
564
+ dict_sensors: dict[str, AmazonDeviceSensor] = {}
565
+ for sensor in sensors["capabilityStates"]:
566
+ sensor_json = orjson.loads(sensor)
567
+ if sensor_json["name"] in SENSORS:
568
+ _value = sensor_json["value"]
569
+ _value_dict = isinstance(_value, dict)
570
+ _name = sensor_json["name"]
571
+ dict_sensors.update(
572
+ {
573
+ _name: AmazonDeviceSensor(
574
+ name=_name,
575
+ value=(_value["value"] if _value_dict else _value),
576
+ scale=_value.get("scale") if _value_dict else None,
577
+ )
578
+ }
579
+ )
580
+ final_sensors.update({_id: dict_sensors})
581
+ return final_sensors
582
+
487
583
  async def login_mode_interactive(self, otp_code: str) -> dict[str, Any]:
488
584
  """Login to Amazon interactively via OTP."""
489
585
  _LOGGER.debug("Logging-in for %s [otp code %s]", self._login_email, otp_code)
@@ -573,7 +669,7 @@ class AmazonEchoApi:
573
669
  self,
574
670
  ) -> dict[str, AmazonDevice]:
575
671
  """Get Amazon devices data."""
576
- devices: dict[str, Any] = {}
672
+ self._devices = {}
577
673
  for key in URI_QUERIES:
578
674
  _, raw_resp = await self._session_request(
579
675
  method=HTTPMethod.GET,
@@ -596,21 +692,32 @@ class AmazonEchoApi:
596
692
 
597
693
  for data in json_data[key]:
598
694
  dev_serial = data.get("serialNumber") or data.get("deviceSerialNumber")
599
- if previous_data := devices.get(dev_serial):
600
- devices[dev_serial] = previous_data | {key: data}
695
+ if previous_data := self._devices.get(dev_serial):
696
+ self._devices[dev_serial] = previous_data | {key: data}
601
697
  else:
602
- devices[dev_serial] = {key: data}
698
+ self._devices[dev_serial] = {key: data}
699
+
700
+ entity_ids_list = await self._get_devices_ids()
701
+ devices_sensors = await self._get_sensors_states(entity_ids_list)
603
702
 
604
703
  final_devices_list: dict[str, AmazonDevice] = {}
605
- for device in devices.values():
704
+ for device in self._devices.values():
606
705
  devices_node = device[NODE_DEVICES]
607
706
  preferences_node = device.get(NODE_PREFERENCES)
608
707
  do_not_disturb_node = device[NODE_DO_NOT_DISTURB]
609
708
  bluetooth_node = device[NODE_BLUETOOTH]
709
+ identifier_node = device.get(NODE_IDENTIFIER, {})
710
+
711
+ # Add sensors
712
+ if identifier_node:
713
+ for _device_id, _device_sensors in devices_sensors.items():
714
+ if _device_id == identifier_node["entityId"]:
715
+ sensors = _device_sensors
716
+
610
717
  # Remove stale, orphaned and virtual devices
611
718
  if (
612
719
  NODE_DEVICES not in device
613
- or devices_node.get("deviceType") == AMAZON_DEVICE_TYPE
720
+ or devices_node.get("deviceType") in DEVICE_TO_IGNORE
614
721
  ):
615
722
  continue
616
723
 
@@ -632,6 +739,9 @@ class AmazonEchoApi:
632
739
  preferences_node["responseStyle"] if preferences_node else None
633
740
  ),
634
741
  bluetooth_state=bluetooth_node["online"],
742
+ entity_id=identifier_node.get("entityId"),
743
+ appliance_id=identifier_node.get("applianceId"),
744
+ sensors=sensors,
635
745
  )
636
746
 
637
747
  self._list_for_clusters.update(
aioamazondevices/const.py CHANGED
@@ -51,22 +51,52 @@ NODE_DEVICES = "devices"
51
51
  NODE_DO_NOT_DISTURB = "doNotDisturbDeviceStatusList"
52
52
  NODE_PREFERENCES = "devicePreferences"
53
53
  NODE_BLUETOOTH = "bluetoothStates"
54
+ NODE_IDENTIFIER = "identifier"
55
+ NODE_SENSORS = "sensors"
54
56
 
55
57
  URI_QUERIES = {
56
58
  NODE_DEVICES: "/api/devices-v2/device",
57
59
  NODE_DO_NOT_DISTURB: "/api/dnd/device-status-list",
58
60
  NODE_PREFERENCES: "/api/device-preferences",
59
61
  NODE_BLUETOOTH: "/api/bluetooth",
62
+ # "/api/ping"
63
+ # "/api/np/command"
64
+ # "/api/np/player"
65
+ # "/api/device-wifi-details"
66
+ # "/api/activities"
67
+ # "/api/behaviors/v2/automations"
68
+ # "/api/notifications"
60
69
  }
61
70
 
71
+ URI_IDS = "/api/phoenix"
72
+ URI_SENSORS = "/api/phoenix/state"
73
+
74
+ SENSORS = [
75
+ "babyCryDetectionState",
76
+ "beepingApplianceDetectionState",
77
+ "coughDetectionState",
78
+ "dogBarkDetectionState",
79
+ "humanPresenceDetectionState",
80
+ "illuminance",
81
+ "temperature",
82
+ "waterSoundsDetectionState",
83
+ ]
84
+ SENSOR_STATE_OFF = "NOT_DETECTED"
85
+
62
86
  # File extensions
63
87
  SAVE_PATH = "out"
64
88
  HTML_EXTENSION = ".html"
65
89
  JSON_EXTENSION = ".json"
66
90
  BIN_EXTENSION = ".bin"
67
91
 
92
+ SPEAKER_GROUP_FAMILY = "WHA"
68
93
  SPEAKER_GROUP_MODEL = "Speaker Group"
69
94
 
95
+ DEVICE_TO_IGNORE: list[str] = [
96
+ AMAZON_DEVICE_TYPE, # Alexa App for Mobile
97
+ "A1RTAM01W29CUP", # Alexa App for PC
98
+ ]
99
+
70
100
  DEVICE_TYPE_TO_MODEL: dict[str, dict[str, str | None]] = {
71
101
  "A10A33FOX2NUBK": {
72
102
  "model": "Echo Spot",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: aioamazondevices
3
- Version: 3.0.9
3
+ Version: 3.1.0
4
4
  Summary: Python library to control Amazon devices
5
5
  License: Apache-2.0
6
6
  Author: Simone Chemelli
@@ -0,0 +1,10 @@
1
+ aioamazondevices/__init__.py,sha256=-bcfJ9SAyLbQjnF7t2qqabZ8Npi0y8IF7CSkvL61RM0,276
2
+ aioamazondevices/api.py,sha256=5usfnZtNvzMiGvb4XcQ6waqoc6FJcLwLGIzBUfcV3Nk,35154
3
+ aioamazondevices/const.py,sha256=cX0w67ct1ho8PZHFO6juFv6u19Cja1d-WI1QJgeahto,5505
4
+ aioamazondevices/exceptions.py,sha256=nPbR928NryTbnxm5QwQpCSpRItvZ3PRYKynUUxH-_v4,1345
5
+ aioamazondevices/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ aioamazondevices/sounds.py,sha256=01pVCDFIuhrLypXInw4JNuHsC6zjMLsuKocet1R6we8,13409
7
+ aioamazondevices-3.1.0.dist-info/LICENSE,sha256=sS48k5sp9bFV-NSHDfAJuTZZ_-AP9ZDqUzQ9sffGlsg,11346
8
+ aioamazondevices-3.1.0.dist-info/METADATA,sha256=GAk_705EuewhyCT_8HzFmrqQCKO2QTa9pqfl-Q4ySCA,5546
9
+ aioamazondevices-3.1.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
10
+ aioamazondevices-3.1.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- aioamazondevices/__init__.py,sha256=NpllEuw05uesvM4TISUrKdxZuuR9lLUDaTbRGceXAl8,276
2
- aioamazondevices/api.py,sha256=kHupOo_Bu9dNLWiXHdJQuHJR47G8olJbn5ukZkSFV6Q,30715
3
- aioamazondevices/const.py,sha256=C6__PALMP3bOn8350uA1jYDShKDkifD6GDn7A9Hlw7Q,4769
4
- aioamazondevices/exceptions.py,sha256=nPbR928NryTbnxm5QwQpCSpRItvZ3PRYKynUUxH-_v4,1345
5
- aioamazondevices/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- aioamazondevices/sounds.py,sha256=01pVCDFIuhrLypXInw4JNuHsC6zjMLsuKocet1R6we8,13409
7
- aioamazondevices-3.0.9.dist-info/LICENSE,sha256=sS48k5sp9bFV-NSHDfAJuTZZ_-AP9ZDqUzQ9sffGlsg,11346
8
- aioamazondevices-3.0.9.dist-info/METADATA,sha256=GrY-IiPzpkN3IjSX5Oyi8wj1dyUVzRtLZl4YCaNB5ZI,5546
9
- aioamazondevices-3.0.9.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
10
- aioamazondevices-3.0.9.dist-info/RECORD,,