rivian-python-client 2.2.0b10__tar.gz → 2.2.0b12__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.
Files changed (103) hide show
  1. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/PKG-INFO +1 -1
  2. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/__version__.py +1 -1
  3. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/body.py +3 -3
  4. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/charging.py +18 -13
  5. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/comfort.py +17 -15
  6. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/departure.py +21 -21
  7. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/device_table.py +24 -6
  8. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/dynamics.py +14 -8
  9. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/energy_edge_compute.py +17 -14
  10. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/holiday_celebration.py +13 -2
  11. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/navigation.py +4 -4
  12. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/ota.py +17 -18
  13. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/body.proto +10 -9
  14. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/body_pb2.py +84 -0
  15. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/charging.proto +8 -3
  16. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/charging_pb2.py +86 -0
  17. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/charging_pb2.pyi +4 -2
  18. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/comfort.proto +3 -3
  19. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/comfort_pb2.py +84 -0
  20. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/device_table.proto +43 -8
  21. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/device_table_pb2.py +60 -0
  22. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/device_table_pb2.pyi +54 -4
  23. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/dynamics.proto +1 -1
  24. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/dynamics_pb2.py +74 -0
  25. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/dynamics_pb2.pyi +4 -4
  26. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/holiday_celebration.proto +13 -4
  27. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/holiday_celebration_pb2.py +56 -0
  28. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/holiday_celebration_pb2.pyi +13 -2
  29. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/navigation.proto +2 -2
  30. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/navigation_pb2.py +76 -0
  31. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/navigation_pb2.pyi +4 -4
  32. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/ota.proto +4 -4
  33. rivian_python_client-2.2.0b12/src/rivian/parallax/proto/ota_pb2.py +78 -0
  34. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/ota_pb2.pyi +6 -6
  35. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/vehicle.proto +2 -2
  36. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/body_pb2.py +0 -84
  37. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/charging_pb2.py +0 -86
  38. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/comfort_pb2.py +0 -84
  39. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/device_table_pb2.py +0 -52
  40. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/dynamics_pb2.py +0 -74
  41. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/holiday_celebration_pb2.py +0 -54
  42. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/navigation_pb2.py +0 -76
  43. rivian_python_client-2.2.0b10/src/rivian/parallax/proto/ota_pb2.py +0 -78
  44. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/.gitignore +0 -0
  45. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/README.md +0 -0
  46. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/pyproject.toml +0 -0
  47. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/__init__.py +0 -0
  48. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/ble.py +0 -0
  49. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/const.py +0 -0
  50. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/exceptions.py +0 -0
  51. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/__init__.py +0 -0
  52. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/core.py +0 -0
  53. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/energy.py +0 -0
  54. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/gearguard_streaming.py +0 -0
  55. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/geofence.py +0 -0
  56. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/parallax.py +0 -0
  57. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/__init__.py +0 -0
  58. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/body_pb2.pyi +0 -0
  59. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/comfort_pb2.pyi +0 -0
  60. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/departure.proto +0 -0
  61. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/departure_pb2.py +0 -0
  62. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/departure_pb2.pyi +0 -0
  63. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/energy.proto +0 -0
  64. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/energy_edge_compute.proto +0 -0
  65. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/energy_edge_compute_pb2.py +0 -0
  66. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/energy_edge_compute_pb2.pyi +0 -0
  67. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/energy_pb2.py +0 -0
  68. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/energy_pb2.pyi +0 -0
  69. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/gearguard_streaming.proto +0 -0
  70. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/gearguard_streaming_pb2.py +0 -0
  71. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/gearguard_streaming_pb2.pyi +0 -0
  72. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/geofence.proto +0 -0
  73. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/geofence_pb2.py +0 -0
  74. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/geofence_pb2.pyi +0 -0
  75. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/parallax.proto +0 -0
  76. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/parallax_pb2.py +0 -0
  77. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/parallax_pb2.pyi +0 -0
  78. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/secure_file_transfer.proto +0 -0
  79. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/secure_file_transfer_pb2.py +0 -0
  80. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/secure_file_transfer_pb2.pyi +0 -0
  81. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/security.proto +0 -0
  82. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/security_pb2.py +0 -0
  83. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/security_pb2.pyi +0 -0
  84. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/user_passcodes.proto +0 -0
  85. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/user_passcodes_pb2.py +0 -0
  86. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/user_passcodes_pb2.pyi +0 -0
  87. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/vehicle_access.proto +0 -0
  88. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/vehicle_access_pb2.py +0 -0
  89. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/vehicle_access_pb2.pyi +0 -0
  90. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/vehicle_pb2.py +0 -0
  91. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/proto/vehicle_pb2.pyi +0 -0
  92. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/secure_file_transfer.py +0 -0
  93. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/security.py +0 -0
  94. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/user_passcodes.py +0 -0
  95. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/vehicle.py +0 -0
  96. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/parallax/vehicle_access.py +0 -0
  97. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/py.typed +0 -0
  98. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/rivian.py +0 -0
  99. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/schemas/charging.graphql +0 -0
  100. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/schemas/gateway.graphql +0 -0
  101. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/schemas/orders.graphql +0 -0
  102. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/utils.py +0 -0
  103. {rivian_python_client-2.2.0b10 → rivian_python_client-2.2.0b12}/src/rivian/ws_monitor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rivian-python-client
3
- Version: 2.2.0b10
3
+ Version: 2.2.0b12
4
4
  Summary: Rivian API Client (Unofficial)
5
5
  Author-email: Brian Retterer <bretterer@gmail.com>, "J.R. Gutierrez" <jrgutier@gmail.com>, Nathan Spencer <natekspencer@gmail.com>, Trevor Mack <drummer8001@gmail.com>
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '2.2.0b10'
4
+ __version__ = VERSION = '2.2.0b12'
@@ -126,8 +126,8 @@ _WINDOW_MAP: Final[dict[int, str]] = {
126
126
 
127
127
  # GraphQL's windowXCalibrated values.
128
128
  _CALIBRATION_MAP: Final[dict[int, str]] = {
129
- body_pb2.CALIBRATION_STATUS_CALIBRATED: "Calibrated",
130
- body_pb2.CALIBRATION_STATUS_NOT_CALIBRATED: "Not_Calibrated",
129
+ body_pb2.CALIBRATION_STATUS_CALIBRATED: "calibrated",
130
+ body_pb2.CALIBRATION_STATUS_NOT_CALIBRATED: "not_calibrated",
131
131
  }
132
132
 
133
133
  _TRAILER_PRESENCE_MAP: Final[dict[int, str]] = {
@@ -204,7 +204,7 @@ def decode_windows(m: body_pb2.WindowsState) -> dict[str, Any]:
204
204
  Fields:
205
205
  windowFrontLeftCalibrated, windowFrontRightCalibrated,
206
206
  windowRearLeftCalibrated, windowRearRightCalibrated,
207
- windowRearCalibrated: str ("Calibrated" | "Not_Calibrated")
207
+ windowRearCalibrated: str ("calibrated" | "not_calibrated")
208
208
  """
209
209
  return {
210
210
  _WINDOW_MAP[w.instance]: _enum(
@@ -362,24 +362,29 @@ _CHARGER_STATUS_MAP: Final[dict[int, str]] = {
362
362
  def decode_energy_state(m: charging_pb2.EnergyState) -> dict[str, Any]:
363
363
  """charging.energy.state — charger connection and charging state.
364
364
 
365
+ Partial updates, so each field only when sent; most messages carry only
366
+ power (not reported; charging.session.power has it). charging.session.status
367
+ is the reliable source for the connection and charging states.
368
+
365
369
  Fields:
366
370
  chargerStatus: str — GraphQL chargerStatus values
367
371
  ("chrgr_sts_not_connected" | "chrgr_sts_connected_no_chrg" |
368
- "chrgr_sts_connected_charging"); inferred
369
- chargerState, connectionState: str | None — as in
370
- charging.session.status
372
+ "chrgr_sts_connected_charging"); inferred. Only sent when the
373
+ plug state changes on some vehicles
374
+ energyChargerState, energyConnectionState: str — as
375
+ charging.session.status's chargerState / connectionState, which
376
+ these can lag
371
377
  _field3: int — raw
372
378
  """
373
- result: dict[str, Any] = {
374
- "chargerState": _enum(
375
- _CHARGING_STATE_MAP, m.charging_state or None, what="charging state"
376
- ),
377
- "connectionState": _enum(
378
- _CONNECTION_STATE_MAP,
379
- m.connection_state or None,
380
- what="charging connection state",
381
- ),
382
- }
379
+ result: dict[str, Any] = {}
380
+ if (v := _present(m, "charging_state")) is not None:
381
+ result["energyChargerState"] = _enum(
382
+ _CHARGING_STATE_MAP, v, what="charging state"
383
+ )
384
+ if (v := _present(m, "connection_state")) is not None:
385
+ result["energyConnectionState"] = _enum(
386
+ _CONNECTION_STATE_MAP, v, what="charging connection state"
387
+ )
383
388
  if (v := _present(m, "charger_status")) is not None:
384
389
  result["chargerStatus"] = _enum(_CHARGER_STATUS_MAP, v, what="charger status")
385
390
  if (v := _present(m, "field_3")) is not None:
@@ -63,10 +63,10 @@ _CLIMATE_HOLD_UNAVAILABILITY_REASON_MAP: Final[dict[int, str]] = {
63
63
  }
64
64
 
65
65
  _DEFROST_MAP: Final[dict[int, str]] = {
66
- comfort_pb2.DEFROST_DEFOG: "Defog",
67
- comfort_pb2.DEFROST_ACTIVE: "Defrost",
68
- comfort_pb2.DEFROST_DEFOG_DEFROST: "Defog_Defrost",
69
- comfort_pb2.DEFROST_OFF: "Off",
66
+ comfort_pb2.DEFROST_DEFOG: "defog",
67
+ comfort_pb2.DEFROST_ACTIVE: "defrost",
68
+ comfort_pb2.DEFROST_DEFOG_DEFROST: "defog_defrost",
69
+ comfort_pb2.DEFROST_OFF: "off",
70
70
  }
71
71
 
72
72
  # GraphQL's cabinPreconditioningStatus / cabinPreconditioningType values.
@@ -132,10 +132,10 @@ _CABIN_SURFACE_MAP: Final[dict[int, str]] = {
132
132
  }
133
133
 
134
134
  _CONDITIONING_LEVEL_MAP: Final[dict[int | None, str]] = {
135
- None: "Off",
136
- comfort_pb2.CONDITIONING_LEVEL_1: "Level_1",
137
- comfort_pb2.CONDITIONING_LEVEL_2: "Level_2",
138
- comfort_pb2.CONDITIONING_LEVEL_3: "Level_3",
135
+ None: "off",
136
+ comfort_pb2.CONDITIONING_LEVEL_1: "level_1",
137
+ comfort_pb2.CONDITIONING_LEVEL_2: "level_2",
138
+ comfort_pb2.CONDITIONING_LEVEL_3: "level_3",
139
139
  }
140
140
 
141
141
  _CONDITIONING_TYPE_MAP: Final[dict[int, str]] = {
@@ -173,13 +173,15 @@ def decode_cabin_temperatures(m: comfort_pb2.CabinTemperatures) -> dict[str, Any
173
173
 
174
174
  Fields:
175
175
  cabinClimateInteriorTemperature: float (°C)
176
- cabinClimateDriverTemperature: float (°C) — set point, same as
177
- `hvacTargetTemperature`
176
+ cabinClimateDriverTemperature: float | None (°C) — set point, same
177
+ as `hvacTargetTemperature`; None when the vehicle doesn't send it
178
178
  cabinClimateExteriorTemperature: float (°C), when sent
179
179
  """
180
180
  result: dict[str, Any] = {
181
181
  "cabinClimateInteriorTemperature": round(m.interior_temperature, 1),
182
- "cabinClimateDriverTemperature": round(m.driver_set_point, 1),
182
+ "cabinClimateDriverTemperature": (
183
+ round(v, 1) if (v := _present(m, "driver_set_point")) is not None else None
184
+ ),
183
185
  }
184
186
  if (v := _present(m, "exterior_temperature")) is not None:
185
187
  result["cabinClimateExteriorTemperature"] = round(v, 1)
@@ -246,8 +248,8 @@ def decode_defrost_status(m: comfort_pb2.DefrostDefogStatus) -> dict[str, Any]:
246
248
  """comfort.cabin.defrost_defog_status — windshield defrost state.
247
249
 
248
250
  Fields:
249
- defrostDefogStatus: str | None ("Defog" | "Defrost" | "Defog_Defrost"
250
- | "Off")
251
+ defrostDefogStatus: str | None ("defog" | "defrost" | "defog_defrost"
252
+ | "off")
251
253
  """
252
254
  return {
253
255
  "defrostDefogStatus": _enum(
@@ -291,8 +293,8 @@ def decode_seat_conditioning(m: comfort_pb2.SeatConditioningStatus) -> dict[str,
291
293
  seatFrontLeftHeat, seatFrontLeftVent, seatFrontRightHeat,
292
294
  seatFrontRightVent, seatRearLeftHeat, seatRearRightHeat,
293
295
  seatThirdRowLeftHeat, seatThirdRowRightHeat: str
294
- ("Off" | "Level_1" | "Level_2" | "Level_3")
295
- steeringWheelHeat: str ("Off" | "Level_1")
296
+ ("off" | "level_1" | "level_2" | "level_3")
297
+ steeringWheelHeat: str ("off" | "level_1")
296
298
  plus <surface>Heat/Vent for any other surface sent (middle seats,
297
299
  rearGlass, frontGlass, sideviewMirrors, wiperArea)
298
300
 
@@ -12,28 +12,28 @@ _Schedules = departure_pb2.DepartureSchedules
12
12
 
13
13
  # Days field -> gateway WeekDay value.
14
14
  _WEEKDAYS = (
15
- ("monday", "Monday"),
16
- ("tuesday", "Tuesday"),
17
- ("wednesday", "Wednesday"),
18
- ("thursday", "Thursday"),
19
- ("friday", "Friday"),
20
- ("saturday", "Saturday"),
21
- ("sunday", "Sunday"),
15
+ ("monday", "monday"),
16
+ ("tuesday", "tuesday"),
17
+ ("wednesday", "wednesday"),
18
+ ("thursday", "thursday"),
19
+ ("friday", "friday"),
20
+ ("saturday", "saturday"),
21
+ ("sunday", "sunday"),
22
22
  )
23
23
 
24
24
  _DEFOG_DEFROST = {
25
- departure_pb2.DEFOG_DEFROST_OFF: "Off",
26
- departure_pb2.DEFOG_DEFROST_DEFOG: "Defog",
27
- departure_pb2.DEFOG_DEFROST_DEFROST: "Defrost",
25
+ departure_pb2.DEFOG_DEFROST_OFF: "off",
26
+ departure_pb2.DEFOG_DEFROST_DEFOG: "defog",
27
+ departure_pb2.DEFOG_DEFROST_DEFROST: "defrost",
28
28
  }
29
29
  _SURFACE_LEVEL = {
30
- departure_pb2.SURFACE_OFF: "Off",
31
- departure_pb2.SURFACE_HEAT_1: "Heat1",
32
- departure_pb2.SURFACE_HEAT_2: "Heat2",
33
- departure_pb2.SURFACE_HEAT_3: "Heat3",
34
- departure_pb2.SURFACE_VENT_1: "Vent1",
35
- departure_pb2.SURFACE_VENT_2: "Vent2",
36
- departure_pb2.SURFACE_VENT_3: "Vent3",
30
+ departure_pb2.SURFACE_OFF: "off",
31
+ departure_pb2.SURFACE_HEAT_1: "heat_1",
32
+ departure_pb2.SURFACE_HEAT_2: "heat_2",
33
+ departure_pb2.SURFACE_HEAT_3: "heat_3",
34
+ departure_pb2.SURFACE_VENT_1: "vent_1",
35
+ departure_pb2.SURFACE_VENT_2: "vent_2",
36
+ departure_pb2.SURFACE_VENT_3: "vent_3",
37
37
  }
38
38
  _SURFACES = (
39
39
  ("front_left_seat", "frontLeftSeat"),
@@ -86,14 +86,14 @@ def decode_departure_schedules(m: departure_pb2.DepartureSchedules) -> dict[str,
86
86
  Fields:
87
87
  departureSchedules: list[dict] — the gateway's DepartureSchedule shape:
88
88
  id: str, name: str, isEnabled: bool
89
- occurrence: {"days": list[str] ("Monday" ... "Sunday"; order
89
+ occurrence: {"days": list[str] ("monday" ... "sunday"; order
90
90
  inferred), "startsAtMin": int (minutes after local midnight)}
91
91
  departureSettings: {"shouldOverrideChargeSchedule": bool,
92
92
  "comfortSettings": {"cabinTempCelsius": float,
93
- "frontDefogDefrost": "Off" | "Defog" | "Defrost",
93
+ "frontDefogDefrost": "off" | "defog" | "defrost",
94
94
  "surfaceHeatVentLevels": {frontLeftSeat, frontRightSeat,
95
- rearLeftSeat, rearRightSeat, steeringWheel: "Off" |
96
- "Heat1" ... "Vent3"}}}
95
+ rearLeftSeat, rearRightSeat, steeringWheel: "off" |
96
+ "heat_1" ... "vent_3"}}}
97
97
  departureSchedulesUpdatedAt: datetime
98
98
  """
99
99
  result: dict[str, Any] = {
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  import uuid
6
6
  from typing import Any, Final
7
7
 
8
+ from ..utils import from_epoch
8
9
  from .core import RVMDecoder, _enum, _present
9
10
  from .proto import device_table_pb2
10
11
 
@@ -59,9 +60,14 @@ def decode_vas_keyper_devices(m: device_table_pb2.VasKeyperDevices) -> dict[str,
59
60
  infoC: int — 1 while active, 2147483647 while inactive, else the
60
61
  pairing attempt number
61
62
  infoA, infoB: int — unknown
63
+ keyPermissions: int — raw
64
+ keyCreatedAt, keyExpiresAt: datetime — when sent and non-zero
65
+ keyMaterial: str — which key material is set ("owner" | "friend" |
66
+ "card" | "fob" | "phone" | "fob2")
62
67
  deviceId: str — hex; for a key card, the GraphQL `devices[].id`
68
+ nfcDeviceId: str — hex; fob2's NFC id (deviceId is its BLE id)
63
69
  phoneId: str — the phone's UUID; phone keys
64
- credentialHex: str — hex; likely key material
70
+ credentialHex: str — hex; the wrapped shared secret
65
71
  deviceOem: str ("unknown" | "rivian" | "apple" | "google" |
66
72
  "samsung" | "vw") — the device maker
67
73
  """
@@ -100,17 +106,29 @@ def decode_vas_keyper_devices(m: device_table_pb2.VasKeyperDevices) -> dict[str,
100
106
  result[key] = v
101
107
  if (v := _present(credentials.info, "status")) is not None:
102
108
  result["keyStatus"] = _enum(_KEY_STATUS_MAP, v, what="key status")
103
- for field in ("card", "fob", "phone"):
104
- if not credentials.HasField(field):
105
- continue
109
+ if (v := _present(credentials.info, "permissions")) is not None:
110
+ result["keyPermissions"] = v
111
+ for field, key in (
112
+ ("created_at", "keyCreatedAt"),
113
+ ("expires_at", "keyExpiresAt"),
114
+ ):
115
+ if v := _present(credentials.info, field):
116
+ result[key] = from_epoch(v)
117
+ if (field := credentials.WhichOneof("material")) is not None:
118
+ result["keyMaterial"] = field
106
119
  material = getattr(credentials, field)
107
- if (ident := _present(material, "id")) is not None:
120
+ id_field, cred_field = (
121
+ ("ble_id", "ble_credential") if field == "fob2" else ("id", "credential")
122
+ )
123
+ if (ident := _present(material, id_field)) is not None:
108
124
  if field == "phone" and len(ident) == 16:
109
125
  result["phoneId"] = str(uuid.UUID(bytes=ident))
110
126
  else:
111
127
  result["deviceId"] = ident.hex()
112
- if (cred := _present(material, "credential")) is not None:
128
+ if (cred := _present(material, cred_field)) is not None:
113
129
  result["credentialHex"] = cred.hex()
130
+ if field == "fob2" and (ident := _present(material, "nfc_id")) is not None:
131
+ result["nfcDeviceId"] = ident.hex()
114
132
  if (v := _present(credentials, "key_type")) is not None:
115
133
  result["keyType"] = _enum(_KEY_TYPE_MAP, v, what="key type")
116
134
  if (v := _present(credentials, "device_oem")) is not None:
@@ -53,9 +53,8 @@ _TEMPERATURE_IMPACT_MAP: Final[dict[int, str]] = {
53
53
  dynamics_pb2.TEMPERATURE_COLD_IMPACT: "cold_impact",
54
54
  }
55
55
 
56
- # "OK" is the GraphQL value for normal.
57
56
  _TIRE_PRESSURE_STATUS_MAP: Final[dict[int, str]] = {
58
- dynamics_pb2.TIRE_PRESSURE_STATUS_NORMAL: "OK",
57
+ dynamics_pb2.TIRE_PRESSURE_STATUS_NORMAL: "ok",
59
58
  dynamics_pb2.TIRE_PRESSURE_STATUS_WARNING_HARD: "warning_hard",
60
59
  dynamics_pb2.TIRE_PRESSURE_STATUS_WARNING_SOFT: "warning_soft",
61
60
  dynamics_pb2.TIRE_PRESSURE_STATUS_WARNING_PUNCTURE: "warning_puncture",
@@ -195,15 +194,21 @@ def decode_range(m: dynamics_pb2.Range) -> dict[str, Any]:
195
194
  }
196
195
 
197
196
 
197
+ # The app's enum class for tire validity was stripped; only 0 and 1 are known.
198
+ _TIRE_VALID: Final = 0
199
+ _TIRE_VALIDITY_MAP: Final[dict[int, str]] = {_TIRE_VALID: "valid", 1: "invalid"}
200
+
201
+
198
202
  @RVMDecoder.register("dynamics.tires.state", dynamics_pb2.TiresState)
199
203
  def decode_tires(m: dynamics_pb2.TiresState) -> dict[str, Any]:
200
204
  """dynamics.tires.state — per-tire pressure, status and validity.
201
205
 
202
206
  Fields:
203
207
  tirePressureFrontLeft, tirePressureFrontRight, etc.: float (bar)
204
- tirePressureStatusFrontLeft, etc.: str ("OK" | "warning_hard" |
208
+ tirePressureStatusFrontLeft, etc.: str ("ok" | "warning_hard" |
205
209
  "warning_soft" | "warning_puncture")
206
- tirePressureStatusValidFrontLeft, etc.: str ("valid" | "invalid")
210
+ tirePressureStatusValidFrontLeft, etc.: str ("valid" | "invalid"),
211
+ or the raw int for an unmapped validity
207
212
  """
208
213
  result: dict[str, Any] = {}
209
214
  for s in m.tire:
@@ -212,10 +217,11 @@ def decode_tires(m: dynamics_pb2.TiresState) -> dict[str, Any]:
212
217
  continue
213
218
  suffix = _TIRE_POSITION_MAP[s.pos]
214
219
 
215
- # Pressure and status are stale while invalid.
216
- valid = not s.invalid
217
- result[f"tirePressureStatusValid{suffix}"] = "valid" if valid else "invalid"
218
- if not valid:
220
+ # Pressure and status are stale unless valid.
221
+ result[f"tirePressureStatusValid{suffix}"] = _enum(
222
+ _TIRE_VALIDITY_MAP, s.validity, what="tire validity"
223
+ )
224
+ if s.validity != _TIRE_VALID:
219
225
  continue
220
226
 
221
227
  if (pressure := _present(s, "pressure")) is not None:
@@ -98,16 +98,19 @@ def decode_charge_session_breakdown(
98
98
 
99
99
  Fields:
100
100
  totalChargedEnergy: float (kWh)
101
- power: float (kW)
101
+ chargeSessionPower: float (kW; charging.session.power reports `power`)
102
102
  rangeAddedThisSession: float (km)
103
103
  kilometersChargedPerHour: float (km of range per hour; 0 while
104
104
  power is 0, since it can be stale)
105
- timeToEndOfCharge: int (minutes)
105
+ chargeSessionTimeToEndOfCharge: int (minutes;
106
+ charging.session.time_estimation reports `timeToEndOfCharge`)
106
107
  activeChargingTime: int (minutes; pauses while stopped/scheduled)
107
108
  packEnergy, thermalEnergy, outletsEnergy, systemEnergy: float (kWh)
108
109
  isFreeSession: bool
109
110
  currentPrice: float, currentCurrency: str — only when there's a cost
110
- chargerState: str — as in charging.session.status
111
+ chargeSessionChargerState: str — the session's state, which goes
112
+ stale after a session (charging.session.status reports
113
+ `chargerState`)
111
114
 
112
115
  A session's totals survive stops and holds, and persist after plug-in
113
116
  until the next session starts. The last message before charging
@@ -116,10 +119,10 @@ def decode_charge_session_breakdown(
116
119
  power = round(m.power, 2)
117
120
  result: dict[str, Any] = {
118
121
  "totalChargedEnergy": round(m.total_energy, 4),
119
- "power": power,
122
+ "chargeSessionPower": power,
120
123
  "rangeAddedThisSession": float(m.range_added),
121
124
  "kilometersChargedPerHour": float(m.range_rate) if power > 0 else 0.0,
122
- "timeToEndOfCharge": m.time_remaining,
125
+ "chargeSessionTimeToEndOfCharge": m.time_remaining,
123
126
  "activeChargingTime": m.active_charging_time,
124
127
  "packEnergy": round(m.pack_energy, 4),
125
128
  "thermalEnergy": round(m.thermal_energy, 4),
@@ -132,7 +135,7 @@ def decode_charge_session_breakdown(
132
135
  result["currentPrice"] = cost.units + cost.nanos / 1e9
133
136
  result["currentCurrency"] = cost.currency_code
134
137
  if m.HasField("charging_state"):
135
- result["chargerState"] = _enum(
138
+ result["chargeSessionChargerState"] = _enum(
136
139
  _CHARGING_STATE_MAP, m.charging_state, what="charging state"
137
140
  )
138
141
  return result
@@ -150,11 +153,11 @@ def decode_charging_graph_global(
150
153
  Fields:
151
154
  startTime: datetime (session start)
152
155
  timeElapsed: int (seconds spent charging)
153
- power: float (kW, latest segment)
154
- kilometersChargedPerHour: float (estimated from power)
156
+ chargingGraphPower: float (kW, latest segment)
157
+ chargingGraphKilometersChargedPerHour: float (estimated from power)
155
158
 
156
- Power updates about once a minute; prefer charge_session_breakdown
157
- for live power and rate.
159
+ Power updates about once a minute; charging.session.power and
160
+ charge_session_breakdown report live power and rate.
158
161
  """
159
162
  segments = m.segment
160
163
  if not segments:
@@ -179,12 +182,12 @@ def decode_charging_graph_global(
179
182
 
180
183
  latest_power = round(segments[-1].power, 2)
181
184
  if latest_power > 0 and segments[-1].state != charging_pb2.CHARGING_USER_STOPPED:
182
- result["power"] = latest_power
183
- result["kilometersChargedPerHour"] = round(
185
+ result["chargingGraphPower"] = latest_power
186
+ result["chargingGraphKilometersChargedPerHour"] = round(
184
187
  latest_power * _FALLBACK_KM_PER_KWH, 1
185
188
  )
186
189
  else:
187
- result["power"] = 0.0
188
- result["kilometersChargedPerHour"] = 0.0
190
+ result["chargingGraphPower"] = 0.0
191
+ result["chargingGraphKilometersChargedPerHour"] = 0.0
189
192
 
190
193
  return result
@@ -20,6 +20,12 @@ def decode_holiday_celebration_enabled(
20
20
  return {}
21
21
 
22
22
 
23
+ _COSTUME_THEME_MAP: Final[dict[int, str]] = {
24
+ holiday_celebration_pb2.CAR_COSTUME_THEME_NONE: "none",
25
+ holiday_celebration_pb2.CAR_COSTUME_THEME_GHOSTBUSTERS: "ghostbusters",
26
+ holiday_celebration_pb2.CAR_COSTUME_THEME_GHOSTBUSTERS_DISPLAY: "ghostbusters_display",
27
+ }
28
+
23
29
  _COSTUME_EFFECT_TRIGGER_MAP: Final[dict[int, str]] = {
24
30
  holiday_celebration_pb2.COSTUME_EFFECT_TRIGGER_MANUAL: "manual",
25
31
  holiday_celebration_pb2.COSTUME_EFFECT_TRIGGER_MOTION: "motion",
@@ -81,14 +87,19 @@ def decode_car_costume_state(
81
87
 
82
88
  Fields:
83
89
  carCostumeAvailability: int
84
- costumeTheme: int
90
+ costumeTheme: str | None ("none" | "ghostbusters" |
91
+ "ghostbusters_display"; inferred), None when unset
85
92
  costumeMotionTriggerDetected: bool
86
93
  activeCostumeEffect: int
87
94
  costumeStartTime: datetime, only when sent
88
95
  """
89
96
  result: dict[str, Any] = {
90
97
  "carCostumeAvailability": m.car_costume_availability,
91
- "costumeTheme": m.costume_theme,
98
+ "costumeTheme": (
99
+ _enum(_COSTUME_THEME_MAP, m.costume_theme, what="costume theme")
100
+ if m.costume_theme
101
+ else None
102
+ ),
92
103
  "costumeMotionTriggerDetected": m.motion_trigger_detected,
93
104
  "activeCostumeEffect": m.active_costume_effect,
94
105
  }
@@ -85,7 +85,7 @@ def _decode_origin(origin: navigation_pb2.GpsFix) -> dict[str, Any]:
85
85
  result = _decode_geocoordinate(
86
86
  _present(origin, "location"), "originLatitude", "originLongitude"
87
87
  )
88
- result["originHeading"] = origin.heading
88
+ result["originBearing"] = origin.bearing
89
89
  if (origin_epoch := _present(origin, "time")) is not None:
90
90
  result["originTime"] = from_epoch(origin_epoch)
91
91
  return result
@@ -282,7 +282,7 @@ def decode_trip_info(m: navigation_pb2.TripInfo) -> dict[str, Any]:
282
282
  tripId: str — changes on every route computation, so it can't
283
283
  correlate messages
284
284
  originLatitude, originLongitude: float
285
- originHeading: float (degrees)
285
+ originBearing: float (degrees)
286
286
  originTime: datetime
287
287
  originStateOfCharge: float (percent)
288
288
  distance, duration: float — driving totals (meters, seconds),
@@ -354,7 +354,7 @@ def decode_trip_progress(m: navigation_pb2.TripProgress) -> dict[str, Any]:
354
354
  (meters, seconds)
355
355
  latitude, longitude: float
356
356
  speed: float (m/s)
357
- heading: float (degrees)
357
+ bearing: float (degrees)
358
358
  locationTime: datetime — time of the position fix
359
359
  """
360
360
  result: dict[str, Any] = {}
@@ -371,7 +371,7 @@ def decode_trip_progress(m: navigation_pb2.TripProgress) -> dict[str, Any]:
371
371
  _decode_geocoordinate(_present(fix, "location"), "latitude", "longitude")
372
372
  )
373
373
  result["speed"] = fix.speed
374
- result["heading"] = fix.heading
374
+ result["bearing"] = fix.bearing
375
375
  if (fix_epoch := _present(fix, "time")) is not None:
376
376
  result["locationTime"] = from_epoch(fix_epoch)
377
377
  return result
@@ -27,24 +27,23 @@ _OTA_STATUS_MAP: Final[dict[int, str]] = {
27
27
 
28
28
 
29
29
  _Deployment = ota_pb2.DeploymentState
30
- # The app's GraphQL-style strings.
31
30
  _CURRENT_STATUS_MAP: Final[dict[int, str]] = {
32
- _Deployment.CURRENT_STATUS_INSTALL_SUCCESS: "Install_Success",
33
- _Deployment.CURRENT_STATUS_INSTALL_FAILED: "Install_Failed",
34
- _Deployment.CURRENT_STATUS_INSTALL_UNABLE_TO_START: "Install_Unable_To_Start",
31
+ _Deployment.CURRENT_STATUS_INSTALL_SUCCESS: "install_success",
32
+ _Deployment.CURRENT_STATUS_INSTALL_FAILED: "install_failed",
33
+ _Deployment.CURRENT_STATUS_INSTALL_UNABLE_TO_START: "install_unable_to_start",
35
34
  }
36
35
 
37
36
  _DEPLOYMENT_INTENT_MAP: Final[dict[int, str]] = {
38
- _Deployment.DEPLOYMENT_INTENT_PERFORMANCE_UPGRADE: "Performance_Upgrade",
39
- _Deployment.DEPLOYMENT_INTENT_BUG_FIX: "Bug_Fix",
40
- _Deployment.DEPLOYMENT_INTENT_SECURITY_UPDATE: "Security_Update",
41
- _Deployment.DEPLOYMENT_INTENT_FEATURE_ADDITION: "Feature_Addition",
37
+ _Deployment.DEPLOYMENT_INTENT_PERFORMANCE_UPGRADE: "performance_upgrade",
38
+ _Deployment.DEPLOYMENT_INTENT_BUG_FIX: "bug_fix",
39
+ _Deployment.DEPLOYMENT_INTENT_SECURITY_UPDATE: "security_update",
40
+ _Deployment.DEPLOYMENT_INTENT_FEATURE_ADDITION: "feature_addition",
42
41
  }
43
42
 
44
43
  _SOFTWARE_CATEGORY_MAP: Final[dict[int, str]] = {
45
- _Deployment.SOFTWARE_CATEGORY_FIRMWARE: "Firmware",
46
- _Deployment.SOFTWARE_CATEGORY_HD_MAPS: "HD_Maps",
47
- _Deployment.SOFTWARE_CATEGORY_VEHICLE_CONFIG: "Vehicle_Config",
44
+ _Deployment.SOFTWARE_CATEGORY_FIRMWARE: "firmware",
45
+ _Deployment.SOFTWARE_CATEGORY_HD_MAPS: "hd_maps",
46
+ _Deployment.SOFTWARE_CATEGORY_VEHICLE_CONFIG: "vehicle_config",
48
47
  }
49
48
 
50
49
 
@@ -56,8 +55,8 @@ def _decode_version(
56
55
  for field, key in (
57
56
  ("version_string", "Version"),
58
57
  ("version_year", "VersionYear"),
59
- ("version_build", "VersionWeek"),
60
- ("build_id", "VersionGitHash"),
58
+ ("version_week", "VersionWeek"),
59
+ ("git_hash", "VersionGitHash"),
61
60
  ):
62
61
  if (v := _present(version, field)) is not None:
63
62
  result[f"{prefix}{key}"] = v
@@ -80,7 +79,7 @@ def decode_deployment_state(m: ota_pb2.DeploymentState) -> dict[str, Any]:
80
79
  otaCurrentVersionYear, otaCurrentVersionWeek,
81
80
  otaCurrentVersionNumber: int
82
81
  otaCurrentVersionGitHash: str
83
- otaSoftwareCategory: str ("Firmware" | "HD_Maps" | "Vehicle_Config")
82
+ otaSoftwareCategory: str ("firmware" | "hd_maps" | "vehicle_config")
84
83
  otaUpdateInProgress: bool
85
84
  otaDeploymentId: str — UUID, only while an update is in flight
86
85
  otaAvailableVersion, otaAvailableVersionYear,
@@ -94,13 +93,13 @@ def decode_deployment_state(m: ota_pb2.DeploymentState) -> dict[str, Any]:
94
93
  otaDownloadProgress, otaInstallProgress: int (0-100)
95
94
  otaTimeRemaining: int (seconds; counts down during
96
95
  "install_countdown", a static default otherwise)
97
- otaCurrentStatus: str | None ("Install_Success" | "Install_Failed" |
98
- "Install_Unable_To_Start") — the last install's result
96
+ otaCurrentStatus: str | None ("install_success" | "install_failed" |
97
+ "install_unable_to_start") — the last install's result
99
98
  otaInstallReady: str ("ota_available" | "ota_not_available")
100
99
  otaInstallDuration: int (minutes)
101
100
  otaInstallTimeOfDay: int (minutes after local midnight), when set
102
- otaDeploymentIntent: str | None ("Performance_Upgrade" | "Bug_Fix" |
103
- "Security_Update" | "Feature_Addition")
101
+ otaDeploymentIntent: str | None ("performance_upgrade" | "bug_fix" |
102
+ "security_update" | "feature_addition")
104
103
  otaSkipAllowed: bool, otaSkipCount: int
105
104
  otaPendingReasons: list[str] — what's blocking an install, e.g.
106
105
  "not_parked", "unplugged", "lv_batt"
@@ -12,16 +12,17 @@ message ClosuresState {
12
12
  int32 open_position_percent = 3;
13
13
  // Unset on doors and windows; briefly GENERAL while re-reporting.
14
14
  ClosureFault fault = 4;
15
- optional WindowsNextAction windows_next_action = 5; // on GROUP_WINDOWS
16
-
17
- // One field per closure type. Briefly SNA / not-available while the
15
+ // One member per closure type. Briefly SNA / not-available while the
18
16
  // vehicle re-reports after an app command.
19
- optional FrunkNextAction frunk_next_action = 6;
20
- optional LiftgateNextAction liftgate_next_action = 7;
21
- optional SideBinNextAction side_bin_next_action = 8;
22
- optional TailgateNextAction tailgate_next_action = 9;
23
- optional ChargePortDoorNextAction charge_port_door_next_action = 10;
24
- int32 tonneau_next_action = 11; // enum type unknown
17
+ oneof next_action {
18
+ WindowsNextAction windows_next_action = 5; // on GROUP_WINDOWS
19
+ FrunkNextAction frunk_next_action = 6;
20
+ LiftgateNextAction liftgate_next_action = 7;
21
+ SideBinNextAction side_bin_next_action = 8;
22
+ TailgateNextAction tailgate_next_action = 9;
23
+ ChargePortDoorNextAction charge_port_door_next_action = 10;
24
+ int32 tonneau_next_action = 11; // the app's enum (class stripped)
25
+ }
25
26
  }
26
27
  repeated Closure closure = 1;
27
28
  }