rivian-python-client 2.3.1__py3-none-any.whl → 2.4.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.
rivian/__version__.py CHANGED
@@ -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.3.1'
4
+ __version__ = VERSION = '2.4.0'
rivian/ble.py CHANGED
@@ -7,7 +7,7 @@ import logging
7
7
  import platform
8
8
  import secrets
9
9
 
10
- from .utils import generate_ble_command_hmac
10
+ from .utils import generate_command_hmac
11
11
 
12
12
  _LOGGER = logging.getLogger(__name__)
13
13
 
@@ -96,7 +96,7 @@ async def pair_phone(
96
96
 
97
97
  _LOGGER.debug("Exchanging nonce")
98
98
  phone_nonce = secrets.token_bytes(16)
99
- hmac = generate_ble_command_hmac(phone_nonce, vehicle_key, private_key)
99
+ hmac = generate_command_hmac(phone_nonce, vehicle_key, private_key)
100
100
  await client.write_gatt_char(
101
101
  PHONE_NONCE_VEHICLE_NONCE_UUID, phone_nonce + hmac
102
102
  )
rivian/const.py CHANGED
@@ -185,8 +185,8 @@ class VehicleCommand(StrEnum):
185
185
  VEHICLE_CABIN_PRECONDITION_DISABLE = "VEHICLE_CABIN_PRECONDITION_DISABLE"
186
186
  VEHICLE_CABIN_PRECONDITION_ENABLE = "VEHICLE_CABIN_PRECONDITION_ENABLE"
187
187
  # Gen2 HVAC Controls
188
- CABIN_HVAC_THIRD_ROW_LEFT_SEAT_HEAT = "CABIN_HVAC_THIRD_ROW_LEFT_SEAT_HEAT"
189
- CABIN_HVAC_THIRD_ROW_RIGHT_SEAT_HEAT = "CABIN_HVAC_THIRD_ROW_RIGHT_SEAT_HEAT"
188
+ CABIN_HVAC_3RD_ROW_REAR_LEFT_SEAT_HEAT = "CABIN_HVAC_3RD_ROW_REAR_LEFT_SEAT_HEAT"
189
+ CABIN_HVAC_3RD_ROW_REAR_RIGHT_SEAT_HEAT = "CABIN_HVAC_3RD_ROW_REAR_RIGHT_SEAT_HEAT"
190
190
 
191
191
  # Closures
192
192
  LOCK_ALL_CLOSURES_FEEDBACK = "LOCK_ALL_CLOSURES_FEEDBACK"
@@ -10,10 +10,18 @@ Public API:
10
10
  encode_vehicle_operation(rvm, payload=b"", ...) -> str
11
11
  Builds a `send_vehicle_operation` request; an empty payload reads
12
12
  the RVM, anything else (e.g. `encode_climate_hold_setting`) writes it.
13
+ encode_command(vehicle_id, rvm, payload, ...) -> str
14
+ Builds and signs a `send_parallax_command` request, with a payload
15
+ such as `encode_unlock()`.
16
+ LOCK_RVM / encode_lock(), UNLOCK_RVM / encode_unlock()
17
+ The RVM and payload of a signed lock or unlock command.
18
+ decode_command_state_message(data) -> dict | None
19
+ Reads a raw `subscribe_for_parallax_command_state` message.
13
20
 
14
21
  Layout:
15
- core.py - registry, payload parsing and shared helpers
16
- proto/ - .proto schemas and their generated *_pb2 modules
22
+ core.py - registry, payload parsing and shared helpers
23
+ command.py - signed remote commands and their state messages
24
+ proto/ - .proto schemas and their generated *_pb2 modules
17
25
  <topic prefix>.py - one module per RVM topic prefix (body.*, etc.)
18
26
 
19
27
  To add a decoder, decorate it with
@@ -44,11 +52,13 @@ from . import ( # noqa: F401 (imported to register their decoders)
44
52
  vehicle,
45
53
  vehicle_access,
46
54
  )
55
+ from .body import LOCK_RVM, UNLOCK_RVM, encode_lock, encode_unlock
47
56
  from .comfort import (
48
57
  CLIMATE_HOLD_SETTING_RVM,
49
58
  CLIMATE_HOLD_STATUS_RVM,
50
59
  encode_climate_hold_setting,
51
60
  )
61
+ from .command import decode_command_state_message, encode_command
52
62
  from .core import (
53
63
  RVMDecoder,
54
64
  decode_parallax_message,
@@ -75,9 +85,15 @@ __all__ = [
75
85
  "CHARGING_RVMS",
76
86
  "CLIMATE_HOLD_SETTING_RVM",
77
87
  "CLIMATE_HOLD_STATUS_RVM",
88
+ "LOCK_RVM",
78
89
  "PARALLAX_RVMS",
90
+ "UNLOCK_RVM",
91
+ "decode_command_state_message",
79
92
  "decode_parallax_message",
80
93
  "decode_parallax_subscription_message",
81
94
  "encode_climate_hold_setting",
95
+ "encode_command",
96
+ "encode_lock",
97
+ "encode_unlock",
82
98
  "encode_vehicle_operation",
83
99
  ]
rivian/parallax/body.py CHANGED
@@ -7,6 +7,9 @@ from typing import Any, Final
7
7
  from .core import _LOGGER, RVMDecoder, _enum, _present
8
8
  from .proto import body_pb2
9
9
 
10
+ LOCK_RVM: Final = "body.locks.lock"
11
+ UNLOCK_RVM: Final = "body.locks.unlock"
12
+
10
13
  _CLOSURE_MAP: Final[dict[int, str]] = {
11
14
  body_pb2.DOOR_FRONT_LEFT: "doorFrontLeftClosed",
12
15
  body_pb2.DOOR_FRONT_RIGHT: "doorFrontRightClosed",
@@ -173,6 +176,16 @@ def decode_closures(m: body_pb2.ClosuresState) -> dict[str, Any]:
173
176
  return result
174
177
 
175
178
 
179
+ def encode_lock() -> bytes:
180
+ """Encode a body.locks.lock payload, which has no fields."""
181
+ return body_pb2.LockCommand().SerializeToString()
182
+
183
+
184
+ def encode_unlock() -> bytes:
185
+ """Encode a body.locks.unlock payload that unlocks everything."""
186
+ return body_pb2.UnlockCommand(all=True).SerializeToString()
187
+
188
+
176
189
  @RVMDecoder.register("body.locks.states", body_pb2.LocksState)
177
190
  def decode_locks(m: body_pb2.LocksState) -> dict[str, Any]:
178
191
  """body.locks.states — lock state of every lock.
@@ -0,0 +1,168 @@
1
+ """Encoder and decoder for signed Parallax remote commands.
2
+
3
+ A command such as `body.locks.unlock` is sent with the `sendParallaxPayload`
4
+ mutation (`Rivian.send_parallax_command`). Its payload is an `Operation` in an
5
+ `Envelope`, signed with the secret that `Rivian.send_vehicle_command` derives from
6
+ the phone's key and the vehicle's, but over the operation's bytes. In testing,
7
+ Rivian woke a sleeping vehicle for it.
8
+
9
+ The `vehicleCommandStatePx` subscription
10
+ (`Rivian.subscribe_for_parallax_command_state`) reports on the command as it
11
+ goes: the vehicle waking up, validating and executing it, then the result.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import base64
17
+ import time
18
+ import uuid
19
+ from typing import Any, Final
20
+
21
+ from google.protobuf.message import DecodeError
22
+
23
+ from ..utils import generate_command_hmac
24
+ from .core import _LOGGER, _debug_message, _enum, _present
25
+ from .proto import command_pb2
26
+
27
+ _PROGRESS_MAP: Final[dict[int, str]] = {
28
+ command_pb2.VEHICLE_MODEL_OP_PROGRESS_UNSPECIFIED: "unspecified",
29
+ command_pb2.VEHICLE_MODEL_OP_PROGRESS_VEHICLE_WAKING_UP: "waking_up",
30
+ command_pb2.VEHICLE_MODEL_OP_PROGRESS_VEHICLE_VALIDATING: "validating",
31
+ command_pb2.VEHICLE_MODEL_OP_PROGRESS_VEHICLE_EXECUTING: "executing",
32
+ command_pb2.VEHICLE_MODEL_OP_PROGRESS_VEHICLE_COMPLETE: "complete",
33
+ }
34
+
35
+ _STATUS_MAP: Final[dict[int, str]] = {
36
+ command_pb2.RESPONSE_STATUS_UNDEFINED: "undefined",
37
+ command_pb2.RESPONSE_STATUS_SUCCESS: "success",
38
+ command_pb2.RESPONSE_STATUS_FAIL: "failed",
39
+ command_pb2.RESPONSE_STATUS_INVALID_REQUEST: "invalid_request",
40
+ command_pb2.RESPONSE_STATUS_NOT_SUPPORTED: "not_supported",
41
+ command_pb2.RESPONSE_STATUS_TIMEOUT: "timeout",
42
+ }
43
+
44
+
45
+ def encode_command(
46
+ vehicle_id: str,
47
+ rvm: str,
48
+ payload: bytes,
49
+ *,
50
+ identity_id: str,
51
+ vehicle_key: str,
52
+ private_key: str,
53
+ command_id: str | None = None,
54
+ timestamp: float | None = None,
55
+ ) -> str:
56
+ """Encode and sign a command, as the payload of `sendParallaxPayload`.
57
+
58
+ `payload` is the RVM's message, e.g. `encode_unlock()` for `UNLOCK_RVM`;
59
+ nothing checks that they go together. `identity_id`, `vehicle_key` and
60
+ `private_key` are the ones `send_vehicle_command` takes. The command id, a
61
+ UUID, is a new one unless `command_id` is given (`ValueError` if it isn't
62
+ one); `timestamp` is epoch milliseconds, now unless given.
63
+
64
+ The result is signed and can be replayed until the vehicle rejects its
65
+ timestamp, so treat it as a secret.
66
+ """
67
+ command_id_bytes = (
68
+ uuid.uuid4().bytes if command_id is None else uuid.UUID(command_id).bytes
69
+ )
70
+ timestamp_ms = int(time.time() * 1000 if timestamp is None else timestamp)
71
+ stamp = command_pb2.Timestamp(
72
+ seconds=timestamp_ms // 1000, nanos=timestamp_ms % 1000 * 1_000_000
73
+ )
74
+ operation = command_pb2.Operation(
75
+ rvm=rvm,
76
+ type=command_pb2.PARALLAX_OPERATION_REQUEST,
77
+ command_id=command_id_bytes,
78
+ payload=payload,
79
+ created_at=stamp,
80
+ vehicle_id=vehicle_id,
81
+ ).SerializeToString()
82
+ envelope = command_pb2.Envelope(
83
+ composite=command_pb2.Composite(bytes_val=operation),
84
+ last_update=stamp,
85
+ correlation_id=command_id_bytes,
86
+ security_info=command_pb2.SecurityInfo(
87
+ signature=command_pb2.Signature(
88
+ type=command_pb2.DATA_SIGNATURE_TYPE_HMAC_SHA256,
89
+ # The raw HMAC-SHA256 of the data, as BLE commands use too
90
+ value=generate_command_hmac(operation, vehicle_key, private_key),
91
+ ),
92
+ mapped_identity=identity_id,
93
+ ),
94
+ )
95
+ return base64.b64encode(envelope.SerializeToString()).decode("utf-8")
96
+
97
+
98
+ def decode_command_state(payload: str) -> dict[str, Any] | None:
99
+ """Decode the payload of a `vehicleCommandStatePx` message.
100
+
101
+ Returns None if the payload is empty or can't be read.
102
+
103
+ Fields:
104
+ commandId: str | None — the command's UUID
105
+ rvm: str
106
+ progress: str | int — "unspecified" | "waking_up" | "validating" |
107
+ "executing" | "complete". Any of the ones before "complete" can be
108
+ skipped, and "waking_up" has come more than once for one command.
109
+ status: str | int | None — how the vehicle answered ("undefined" |
110
+ "success" | "failed" | "invalid_request" | "not_supported" |
111
+ "timeout"); None if the message has no result
112
+ error: dict | None — {"code": int, "message": str}, from the result or,
113
+ if the whole operation failed, from the operation error (which has
114
+ no status)
115
+
116
+ A value this library doesn't know comes back as the number it was sent as.
117
+
118
+ The vehicle has done the command when the progress is "complete", the status
119
+ "success" and there is no error. "failed", "invalid_request" and
120
+ "not_supported" mean it has not. With "timeout" or "undefined", or if no
121
+ "complete" message comes at all, it isn't known, and the vehicle may do it
122
+ later.
123
+ """
124
+ try:
125
+ data = base64.b64decode(payload)
126
+ message = command_pb2.CommandState.FromString(data)
127
+ except (TypeError, ValueError, DecodeError):
128
+ _LOGGER.debug("Failed to decode command state", exc_info=True)
129
+ return None
130
+ if not data:
131
+ return None
132
+ _debug_message("command state", message)
133
+
134
+ status: str | int | None = None
135
+ error: dict[str, Any] | None = None
136
+ if (result := _present(message, "data")) is not None:
137
+ status = _enum(_STATUS_MAP, result.response_status, what="command status")
138
+ if (result_error := _present(result, "error")) is not None:
139
+ error = {"code": result_error.code, "message": result_error.message}
140
+ elif (operation_error := _present(message, "operation_error")) is not None:
141
+ error = {"code": operation_error.error, "message": operation_error.message}
142
+ return {
143
+ "commandId": (
144
+ str(uuid.UUID(bytes=message.command_id))
145
+ if len(message.command_id) == 16
146
+ else None
147
+ ),
148
+ "rvm": message.rvm,
149
+ "progress": _enum(_PROGRESS_MAP, message.progress, what="command progress"),
150
+ "status": status,
151
+ "error": error,
152
+ }
153
+
154
+
155
+ def decode_command_state_message(data: dict[str, Any]) -> dict[str, Any] | None:
156
+ """Decode a raw `subscribe_for_parallax_command_state` message.
157
+
158
+ Unwraps `payload.data.vehicleCommandStatePx.payload` for
159
+ `decode_command_state`; returns None if it's missing or can't be read. That
160
+ includes a message that carries GraphQL errors (`data["payload"]["errors"]`)
161
+ instead of a state.
162
+ """
163
+ payload: Any = data
164
+ for key in ("payload", "data", "vehicleCommandStatePx", "payload"):
165
+ if not isinstance(payload, dict):
166
+ return None
167
+ payload = payload.get(key)
168
+ return decode_command_state(payload)
rivian/parallax/core.py CHANGED
@@ -76,6 +76,16 @@ def _describe_unknowns(message: Message, path: str = "") -> list[str]:
76
76
  return found
77
77
 
78
78
 
79
+ def _debug_message(label: str, message: Message) -> None:
80
+ """Debug-log a parsed `message` and anything its schema doesn't define."""
81
+ if _LOGGER.isEnabledFor(logging.DEBUG):
82
+ _LOGGER.debug(
83
+ "%s: %s", label, text_format.MessageToString(message, as_one_line=True)
84
+ )
85
+ for unknown in _describe_unknowns(message):
86
+ _LOGGER.debug("%s: %s", label, unknown)
87
+
88
+
79
89
  class RVMDecoder:
80
90
  """Registry of RVM topic -> (message class, decoder).
81
91
 
@@ -124,12 +134,7 @@ def _decode_rvm_payload(payload: str | None, rvm: str) -> dict[str, Any]:
124
134
  except Exception:
125
135
  _LOGGER.debug("Failed to decode %s payload", rvm, exc_info=True)
126
136
  return {}
127
- if _LOGGER.isEnabledFor(logging.DEBUG):
128
- _LOGGER.debug(
129
- "%s: %s", rvm, text_format.MessageToString(message, as_one_line=True)
130
- )
131
- for unknown in _describe_unknowns(message):
132
- _LOGGER.debug("%s: %s", rvm, unknown)
137
+ _debug_message(rvm, message)
133
138
  return result
134
139
 
135
140
 
@@ -266,3 +266,14 @@ enum WiperFluidState {
266
266
  WIPER_FLUID_STATE_LOW = 2;
267
267
  WIPER_FLUID_STATE_EMPTY = 3;
268
268
  }
269
+
270
+ // body.locks.unlock, the payload of a signed command (see ../command.py)
271
+ message UnlockCommand {
272
+ oneof target {
273
+ bool all = 1; // the app always sets this
274
+ bool user_preference = 2; // in the app's schema; its button never sends it
275
+ }
276
+ }
277
+
278
+ // body.locks.lock, the payload of a signed command. It has no fields.
279
+ message LockCommand {}
@@ -24,47 +24,47 @@ _sym_db = _symbol_database.Default()
24
24
 
25
25
 
26
26
 
27
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n rivian/parallax/proto/body.proto\x12\x14rivian.parallax.body\"\x89\x06\n\rClosuresState\x12<\n\x07\x63losure\x18\x01 \x03(\x0b\x32+.rivian.parallax.body.ClosuresState.Closure\x1a\xb9\x05\n\x07\x43losure\x12+\n\x02id\x18\x01 \x01(\x0e\x32\x1f.rivian.parallax.body.ClosureId\x12\x36\n\x05state\x18\x02 \x01(\x0e\x32\".rivian.parallax.body.ClosureStateH\x01\x88\x01\x01\x12\x1d\n\x15open_position_percent\x18\x03 \x01(\x05\x12\x31\n\x05\x66\x61ult\x18\x04 \x01(\x0e\x32\".rivian.parallax.body.ClosureFault\x12\x46\n\x13windows_next_action\x18\x05 \x01(\x0e\x32\'.rivian.parallax.body.WindowsNextActionH\x00\x12\x42\n\x11\x66runk_next_action\x18\x06 \x01(\x0e\x32%.rivian.parallax.body.FrunkNextActionH\x00\x12H\n\x14liftgate_next_action\x18\x07 \x01(\x0e\x32(.rivian.parallax.body.LiftgateNextActionH\x00\x12G\n\x14side_bin_next_action\x18\x08 \x01(\x0e\x32\'.rivian.parallax.body.SideBinNextActionH\x00\x12H\n\x14tailgate_next_action\x18\t \x01(\x0e\x32(.rivian.parallax.body.TailgateNextActionH\x00\x12V\n\x1c\x63harge_port_door_next_action\x18\n \x01(\x0e\x32..rivian.parallax.body.ChargePortDoorNextActionH\x00\x12\x1d\n\x13tonneau_next_action\x18\x0b \x01(\x05H\x00\x42\r\n\x0bnext_actionB\x08\n\x06_state\"\xe3\x01\n\nLocksState\x12\x33\n\x04lock\x18\x01 \x03(\x0b\x32%.rivian.parallax.body.LocksState.Lock\x1a\x9f\x01\n\x04Lock\x12(\n\x02id\x18\x01 \x01(\x0e\x32\x1c.rivian.parallax.body.LockId\x12\x33\n\x05state\x18\x02 \x01(\x0e\x32\x1f.rivian.parallax.body.LockStateH\x00\x88\x01\x01\x12.\n\x05\x66\x61ult\x18\x03 \x01(\x0e\x32\x1f.rivian.parallax.body.LockFaultB\x08\n\x06_state\"\xd1\x01\n\x0cWindowsState\x12\x39\n\x06window\x18\x01 \x03(\x0b\x32).rivian.parallax.body.WindowsState.Window\x1a\x85\x01\n\x06Window\x12\x36\n\x08instance\x18\x01 \x01(\x0e\x32$.rivian.parallax.body.WindowInstance\x12\x43\n\x12\x63\x61libration_status\x18\x02 \x01(\x0e\x32\'.rivian.parallax.body.CalibrationStatus\"\x9b\x01\n\x0cTrailerState\x12<\n\x08presence\x18\x01 \x01(\x0e\x32%.rivian.parallax.body.TrailerPresenceH\x00\x88\x01\x01\x12@\n\x11rear_hitch_status\x18\x02 \x01(\x0e\x32%.rivian.parallax.body.RearHitchStatusB\x0b\n\t_presence\"G\n\x0fWiperFluidLevel\x12\x34\n\x05state\x18\x02 \x01(\x0e\x32%.rivian.parallax.body.WiperFluidState*\xe8\x02\n\tClosureId\x12\x1a\n\x16\x43LOSURE_ID_UNSPECIFIED\x10\x00\x12\x13\n\x0f\x44OOR_FRONT_LEFT\x10\x01\x12\x14\n\x10\x44OOR_FRONT_RIGHT\x10\x02\x12\x12\n\x0e\x44OOR_REAR_LEFT\x10\x03\x12\x13\n\x0f\x44OOR_REAR_RIGHT\x10\x04\x12\t\n\x05\x46RUNK\x10\x05\x12\x0c\n\x08TAILGATE\x10\x06\x12\x0c\n\x08LIFTGATE\x10\x07\x12\x11\n\rSIDE_BIN_LEFT\x10\x08\x12\x12\n\x0eSIDE_BIN_RIGHT\x10\t\x12\x0f\n\x0b\x43HARGE_PORT\x10\n\x12\x0b\n\x07TONNEAU\x10\x0b\x12\x15\n\x11WINDOW_FRONT_LEFT\x10\x0c\x12\x16\n\x12WINDOW_FRONT_RIGHT\x10\r\x12\x14\n\x10WINDOW_REAR_LEFT\x10\x0e\x12\x15\n\x11WINDOW_REAR_RIGHT\x10\x0f\x12\x0f\n\x0bWINDOW_REAR\x10\x10\x12\x12\n\rGROUP_WINDOWS\x10\x90N*\xad\x01\n\x0c\x43losureState\x12\x1d\n\x19\x43LOSURE_STATE_UNSPECIFIED\x10\x00\x12\x16\n\x12\x43LOSURE_STATE_OPEN\x10\x01\x12\x18\n\x14\x43LOSURE_STATE_CLOSED\x10\x02\x12\x16\n\x12\x43LOSURE_STATE_AJAR\x10\x03\x12\x19\n\x15\x43LOSURE_STATE_OPENING\x10\x04\x12\x19\n\x15\x43LOSURE_STATE_CLOSING\x10\x05*\xf3\x02\n\x06LockId\x12\x17\n\x13LOCK_ID_UNSPECIFIED\x10\x00\x12\x18\n\x14LOCK_DOOR_FRONT_LEFT\x10\x01\x12\x19\n\x15LOCK_DOOR_FRONT_RIGHT\x10\x02\x12\x17\n\x13LOCK_DOOR_REAR_LEFT\x10\x03\x12\x18\n\x14LOCK_DOOR_REAR_RIGHT\x10\x04\x12\x0e\n\nLOCK_FRUNK\x10\x05\x12\x11\n\rLOCK_TAILGATE\x10\x06\x12\x11\n\rLOCK_LIFTGATE\x10\x07\x12\x16\n\x12LOCK_SIDE_BIN_LEFT\x10\x08\x12\x17\n\x13LOCK_SIDE_BIN_RIGHT\x10\t\x12\x14\n\x10LOCK_CHARGE_PORT\x10\n\x12\x17\n\x13LOCK_TRUNK_SECURITY\x10\x0b\x12\x17\n\x13LOCK_CENTER_CONSOLE\x10\x0c\x12\x12\n\x0eLOCK_GLOVE_BOX\x10\r\x12\x13\n\x0fLOCK_GEAR_GUARD\x10\x0e\x12\x10\n\x0cLOCK_TONNEAU\x10\x0f*z\n\tLockState\x12\x1a\n\x16LOCK_STATE_UNSPECIFIED\x10\x00\x12\x15\n\x11LOCK_STATE_LOCKED\x10\x01\x12\x17\n\x13LOCK_STATE_UNLOCKED\x10\x02\x12!\n\x1dLOCK_STATE_PARTIALLY_UNLOCKED\x10\x03*?\n\tLockFault\x12\x1a\n\x16LOCK_FAULT_UNSPECIFIED\x10\x00\x12\x16\n\x12LOCK_FAULT_FAULTED\x10\x01*\xcb\x01\n\x0eWindowInstance\x12\x1f\n\x1bWINDOW_INSTANCE_UNSPECIFIED\x10\x00\x12\x1e\n\x1aWINDOW_INSTANCE_FRONT_LEFT\x10\x01\x12\x1f\n\x1bWINDOW_INSTANCE_FRONT_RIGHT\x10\x02\x12\x1d\n\x19WINDOW_INSTANCE_REAR_LEFT\x10\x03\x12\x1e\n\x1aWINDOW_INSTANCE_REAR_RIGHT\x10\x04\x12\x18\n\x14WINDOW_INSTANCE_REAR\x10\x05*\x81\x01\n\x11\x43\x61librationStatus\x12\"\n\x1e\x43\x41LIBRATION_STATUS_UNSPECIFIED\x10\x00\x12!\n\x1d\x43\x41LIBRATION_STATUS_CALIBRATED\x10\x01\x12%\n!CALIBRATION_STATUS_NOT_CALIBRATED\x10\x02*\xd8\x01\n\x0fRearHitchStatus\x12!\n\x1dREAR_HITCH_STATUS_UNSPECIFIED\x10\x00\x12!\n\x1dREAR_HITCH_STATUS_NOT_PRESENT\x10\x01\x12\x1f\n\x1bREAR_HITCH_STATUS_ACCESSORY\x10\x02\x12\x1e\n\x1aREAR_HITCH_STATUS_TRAILER1\x10\x03\x12\x1e\n\x1aREAR_HITCH_STATUS_TRAILER2\x10\x04\x12\x1e\n\x1aREAR_HITCH_STATUS_TRAILER3\x10\x05*\xaa\x01\n\x0c\x43losureFault\x12\x1d\n\x19\x43LOSURE_FAULT_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43LOSURE_FAULT_NO_FAULT\x10\x01\x12\x19\n\x15\x43LOSURE_FAULT_GENERAL\x10\x02\x12&\n\"CLOSURE_FAULT_NO_POWERED_OPERATION\x10\x03\x12\x1c\n\x18\x43LOSURE_FAULT_OBSTRUCTED\x10\x04*\x97\x01\n\x0fTrailerPresence\x12 \n\x1cTRAILER_PRESENCE_UNSPECIFIED\x10\x00\x12\x17\n\x13TRAILER_NOT_PRESENT\x10\x01\x12\x13\n\x0fTRAILER_PRESENT\x10\x02\x12\x1f\n\x1bTRAILER_PRESENT_WITH_BRAKES\x10\x03\x12\x13\n\x0fTRAILER_INVALID\x10\x04*\x9f\x04\n\x18\x43hargePortDoorNextAction\x12\x18\n\x14\x43HARGE_PORT_DOOR_SNA\x10\x00\x12!\n\x1d\x43HARGE_PORT_DOOR_OPEN_ALLOWED\x10\x01\x12\'\n#CHARGE_PORT_DOOR_OPEN_NOT_AVAILABLE\x10\x02\x12-\n)CHARGE_PORT_DOOR_OPEN_NOT_ALLOWED_FAULTED\x10\x03\x12\x1c\n\x18\x43HARGE_PORT_DOOR_OPENING\x10\x04\x12\"\n\x1e\x43HARGE_PORT_DOOR_CLOSE_ALLOWED\x10\x05\x12;\n7CHARGE_PORT_DOOR_OBSTRUCTED_WHILE_OPENING_CLOSE_ALLOWED\x10\x06\x12:\n6CHARGE_PORT_DOOR_OBSTRUCTED_WHILE_OPENING_OPEN_ALLOWED\x10\x07\x12;\n7CHARGE_PORT_DOOR_OBSTRUCTED_WHILE_CLOSING_CLOSE_ALLOWED\x10\x08\x12(\n$CHARGE_PORT_DOOR_CLOSE_NOT_AVAILABLE\x10\t\x12.\n*CHARGE_PORT_DOOR_CLOSE_NOT_ALLOWED_FAULTED\x10\n\x12\x1c\n\x18\x43HARGE_PORT_DOOR_CLOSING\x10\x0b*\xb0\x03\n\x0f\x46runkNextAction\x12\r\n\tFRUNK_SNA\x10\x00\x12\x16\n\x12\x46RUNK_OPEN_ALLOWED\x10\x01\x12\x17\n\x13\x46RUNK_CLOSE_ALLOWED\x10\x02\x12\x11\n\rFRUNK_OPENING\x10\x03\x12\x11\n\rFRUNK_CLOSING\x10\x04\x12\x1c\n\x18\x46RUNK_OPEN_NOT_AVAILABLE\x10\x05\x12\x1d\n\x19\x46RUNK_CLOSE_NOT_AVAILABLE\x10\x06\x12\"\n\x1e\x46RUNK_OPEN_NOT_ALLOWED_FAULTED\x10\x07\x12#\n\x1f\x46RUNK_CLOSE_NOT_ALLOWED_FAULTED\x10\x08\x12)\n%FRUNK_OPEN_ALLOWED_NO_POWER_OPERATION\x10\t\x12.\n*FRUNK_CLOSE_NOT_ALLOWED_NO_POWER_OPERATION\x10\n\x12*\n&FRUNK_OBSTRUCTED_OPENING_CLOSE_ALLOWED\x10\x0b\x12*\n&FRUNK_OBSTRUCTED_CLOSING_CLOSE_ALLOWED\x10\x0c*\xa7\x06\n\x12LiftgateNextAction\x12\x10\n\x0cLIFTGATE_SNA\x10\x00\x12\x19\n\x15LIFTGATE_OPEN_ALLOWED\x10\x01\x12\x1a\n\x16LIFTGATE_CLOSE_ALLOWED\x10\x02\x12\x14\n\x10LIFTGATE_OPENING\x10\x03\x12\x14\n\x10LIFTGATE_CLOSING\x10\x04\x12\x1f\n\x1bLIFTGATE_OPEN_NOT_AVAILABLE\x10\x05\x12 \n\x1cLIFTGATE_CLOSE_NOT_AVAILABLE\x10\x06\x12%\n!LIFTGATE_OPEN_NOT_ALLOWED_FAULTED\x10\x07\x12&\n\"LIFTGATE_CLOSE_NOT_ALLOWED_FAULTED\x10\x08\x12,\n(LIFTGATE_OPEN_ALLOWED_NO_POWER_OPERATION\x10\t\x12\x31\n-LIFTGATE_CLOSE_NOT_ALLOWED_NO_POWER_OPERATION\x10\n\x12-\n)LIFTGATE_OBSTRUCTED_OPENING_CLOSE_ALLOWED\x10\x0b\x12-\n)LIFTGATE_OBSTRUCTED_CLOSING_CLOSE_ALLOWED\x10\x0c\x12.\n*LIFTGATE_LOWER_GATE_OPEN_CLOSE_NOT_ALLOWED\x10\r\x12&\n\"LIFTGATE_OPENING_PAUSE_NOT_ALLOWED\x10\x0e\x12&\n\"LIFTGATE_CLOSING_PAUSE_NOT_ALLOWED\x10\x0f\x12+\n\'LIFTGATE_OPEN_ALLOWED_OBSTACLE_DETECTED\x10\x10\x12*\n&LIFTGATE_OPEN_ALLOWED_TRAILER_DETECTED\x10\x11\x12,\n(LIFTGATE_CLOSE_ALLOWED_OBSTACLE_DETECTED\x10\x12\x12+\n\'LIFTGATE_CLOSE_ALLOWED_TRAILER_DETECTED\x10\x13\x12\x17\n\x13LIFTGATE_PROCESSING\x10\x14*\xb2\x02\n\x11SideBinNextAction\x12\x10\n\x0cSIDE_BIN_SNA\x10\x00\x12\x19\n\x15SIDE_BIN_OPEN_ALLOWED\x10\x01\x12\x14\n\x10SIDE_BIN_OPENING\x10\x02\x12\x1f\n\x1bSIDE_BIN_OPEN_NOT_AVAILABLE\x10\x03\x12%\n!SIDE_BIN_OPEN_NOT_ALLOWED_FAULTED\x10\x04\x12\x32\n.SIDE_BIN_STUCK_AJAR_WHILE_OPENING_OPEN_ALLOWED\x10\x05\x12-\n)SIDE_BIN_OPEN_ALREADY_NO_ACTION_AVAILABLE\x10\x06\x12/\n+SIDE_BIN_OPEN_ALLOWED_CONFIRM_VEHICLE_ANGLE\x10\x07*\x8c\x03\n\x12TailgateNextAction\x12\x10\n\x0cTAILGATE_SNA\x10\x00\x12\x19\n\x15TAILGATE_OPEN_ALLOWED\x10\x01\x12\x14\n\x10TAILGATE_OPENING\x10\x02\x12\x1f\n\x1bTAILGATE_OPEN_NOT_AVAILABLE\x10\x03\x12%\n!TAILGATE_OPEN_NOT_ALLOWED_FAULTED\x10\x04\x12\x32\n.TAILGATE_STUCK_AJAR_WHILE_OPENING_OPEN_ALLOWED\x10\x05\x12-\n)TAILGATE_OPEN_ALREADY_NO_ACTION_AVAILABLE\x10\x06\x12/\n+TAILGATE_OPEN_ALLOWED_CONFIRM_VEHICLE_ANGLE\x10\x07\x12+\n\'TAILGATE_OPEN_ALLOWED_OBSTACLE_DETECTED\x10\x08\x12*\n&TAILGATE_OPEN_ALLOWED_TRAILER_DETECTED\x10\t*\xb0\x03\n\x11WindowsNextAction\x12\x0f\n\x0bWINDOWS_SNA\x10\x00\x12\x18\n\x14WINDOWS_OPEN_ALLOWED\x10\x01\x12\x19\n\x15WINDOWS_CLOSE_ALLOWED\x10\x02\x12\x13\n\x0fWINDOWS_OPENING\x10\x03\x12\x13\n\x0fWINDOWS_CLOSING\x10\x04\x12\x12\n\x0eWINDOWS_MOVING\x10\x05\x12\x1e\n\x1aWINDOWS_OPEN_NOT_AVAILABLE\x10\x06\x12\x1f\n\x1bWINDOWS_CLOSE_NOT_AVAILABLE\x10\x07\x12$\n WINDOWS_OPEN_NOT_ALLOWED_FAULTED\x10\x08\x12%\n!WINDOWS_CLOSE_NOT_ALLOWED_FAULTED\x10\t\x12\x32\n.WINDOWS_OBSTRUCTED_WHILE_CLOSING_CLOSE_ALLOWED\x10\n\x12*\n&WINDOWS_CLOSE_NOT_ALLOWED_UNCALIBRATED\x10\x0b\x12)\n%WINDOWS_OPEN_NOT_ALLOWED_UNCALIBRATED\x10\x0c*\x8a\x01\n\x0fWiperFluidState\x12!\n\x1dWIPER_FLUID_STATE_UNSPECIFIED\x10\x00\x12\x1c\n\x18WIPER_FLUID_STATE_NORMAL\x10\x01\x12\x19\n\x15WIPER_FLUID_STATE_LOW\x10\x02\x12\x1b\n\x17WIPER_FLUID_STATE_EMPTY\x10\x03\x62\x06proto3')
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n rivian/parallax/proto/body.proto\x12\x14rivian.parallax.body\"\x89\x06\n\rClosuresState\x12<\n\x07\x63losure\x18\x01 \x03(\x0b\x32+.rivian.parallax.body.ClosuresState.Closure\x1a\xb9\x05\n\x07\x43losure\x12+\n\x02id\x18\x01 \x01(\x0e\x32\x1f.rivian.parallax.body.ClosureId\x12\x36\n\x05state\x18\x02 \x01(\x0e\x32\".rivian.parallax.body.ClosureStateH\x01\x88\x01\x01\x12\x1d\n\x15open_position_percent\x18\x03 \x01(\x05\x12\x31\n\x05\x66\x61ult\x18\x04 \x01(\x0e\x32\".rivian.parallax.body.ClosureFault\x12\x46\n\x13windows_next_action\x18\x05 \x01(\x0e\x32\'.rivian.parallax.body.WindowsNextActionH\x00\x12\x42\n\x11\x66runk_next_action\x18\x06 \x01(\x0e\x32%.rivian.parallax.body.FrunkNextActionH\x00\x12H\n\x14liftgate_next_action\x18\x07 \x01(\x0e\x32(.rivian.parallax.body.LiftgateNextActionH\x00\x12G\n\x14side_bin_next_action\x18\x08 \x01(\x0e\x32\'.rivian.parallax.body.SideBinNextActionH\x00\x12H\n\x14tailgate_next_action\x18\t \x01(\x0e\x32(.rivian.parallax.body.TailgateNextActionH\x00\x12V\n\x1c\x63harge_port_door_next_action\x18\n \x01(\x0e\x32..rivian.parallax.body.ChargePortDoorNextActionH\x00\x12\x1d\n\x13tonneau_next_action\x18\x0b \x01(\x05H\x00\x42\r\n\x0bnext_actionB\x08\n\x06_state\"\xe3\x01\n\nLocksState\x12\x33\n\x04lock\x18\x01 \x03(\x0b\x32%.rivian.parallax.body.LocksState.Lock\x1a\x9f\x01\n\x04Lock\x12(\n\x02id\x18\x01 \x01(\x0e\x32\x1c.rivian.parallax.body.LockId\x12\x33\n\x05state\x18\x02 \x01(\x0e\x32\x1f.rivian.parallax.body.LockStateH\x00\x88\x01\x01\x12.\n\x05\x66\x61ult\x18\x03 \x01(\x0e\x32\x1f.rivian.parallax.body.LockFaultB\x08\n\x06_state\"\xd1\x01\n\x0cWindowsState\x12\x39\n\x06window\x18\x01 \x03(\x0b\x32).rivian.parallax.body.WindowsState.Window\x1a\x85\x01\n\x06Window\x12\x36\n\x08instance\x18\x01 \x01(\x0e\x32$.rivian.parallax.body.WindowInstance\x12\x43\n\x12\x63\x61libration_status\x18\x02 \x01(\x0e\x32\'.rivian.parallax.body.CalibrationStatus\"\x9b\x01\n\x0cTrailerState\x12<\n\x08presence\x18\x01 \x01(\x0e\x32%.rivian.parallax.body.TrailerPresenceH\x00\x88\x01\x01\x12@\n\x11rear_hitch_status\x18\x02 \x01(\x0e\x32%.rivian.parallax.body.RearHitchStatusB\x0b\n\t_presence\"G\n\x0fWiperFluidLevel\x12\x34\n\x05state\x18\x02 \x01(\x0e\x32%.rivian.parallax.body.WiperFluidState\"C\n\rUnlockCommand\x12\r\n\x03\x61ll\x18\x01 \x01(\x08H\x00\x12\x19\n\x0fuser_preference\x18\x02 \x01(\x08H\x00\x42\x08\n\x06target\"\r\n\x0bLockCommand*\xe8\x02\n\tClosureId\x12\x1a\n\x16\x43LOSURE_ID_UNSPECIFIED\x10\x00\x12\x13\n\x0f\x44OOR_FRONT_LEFT\x10\x01\x12\x14\n\x10\x44OOR_FRONT_RIGHT\x10\x02\x12\x12\n\x0e\x44OOR_REAR_LEFT\x10\x03\x12\x13\n\x0f\x44OOR_REAR_RIGHT\x10\x04\x12\t\n\x05\x46RUNK\x10\x05\x12\x0c\n\x08TAILGATE\x10\x06\x12\x0c\n\x08LIFTGATE\x10\x07\x12\x11\n\rSIDE_BIN_LEFT\x10\x08\x12\x12\n\x0eSIDE_BIN_RIGHT\x10\t\x12\x0f\n\x0b\x43HARGE_PORT\x10\n\x12\x0b\n\x07TONNEAU\x10\x0b\x12\x15\n\x11WINDOW_FRONT_LEFT\x10\x0c\x12\x16\n\x12WINDOW_FRONT_RIGHT\x10\r\x12\x14\n\x10WINDOW_REAR_LEFT\x10\x0e\x12\x15\n\x11WINDOW_REAR_RIGHT\x10\x0f\x12\x0f\n\x0bWINDOW_REAR\x10\x10\x12\x12\n\rGROUP_WINDOWS\x10\x90N*\xad\x01\n\x0c\x43losureState\x12\x1d\n\x19\x43LOSURE_STATE_UNSPECIFIED\x10\x00\x12\x16\n\x12\x43LOSURE_STATE_OPEN\x10\x01\x12\x18\n\x14\x43LOSURE_STATE_CLOSED\x10\x02\x12\x16\n\x12\x43LOSURE_STATE_AJAR\x10\x03\x12\x19\n\x15\x43LOSURE_STATE_OPENING\x10\x04\x12\x19\n\x15\x43LOSURE_STATE_CLOSING\x10\x05*\xf3\x02\n\x06LockId\x12\x17\n\x13LOCK_ID_UNSPECIFIED\x10\x00\x12\x18\n\x14LOCK_DOOR_FRONT_LEFT\x10\x01\x12\x19\n\x15LOCK_DOOR_FRONT_RIGHT\x10\x02\x12\x17\n\x13LOCK_DOOR_REAR_LEFT\x10\x03\x12\x18\n\x14LOCK_DOOR_REAR_RIGHT\x10\x04\x12\x0e\n\nLOCK_FRUNK\x10\x05\x12\x11\n\rLOCK_TAILGATE\x10\x06\x12\x11\n\rLOCK_LIFTGATE\x10\x07\x12\x16\n\x12LOCK_SIDE_BIN_LEFT\x10\x08\x12\x17\n\x13LOCK_SIDE_BIN_RIGHT\x10\t\x12\x14\n\x10LOCK_CHARGE_PORT\x10\n\x12\x17\n\x13LOCK_TRUNK_SECURITY\x10\x0b\x12\x17\n\x13LOCK_CENTER_CONSOLE\x10\x0c\x12\x12\n\x0eLOCK_GLOVE_BOX\x10\r\x12\x13\n\x0fLOCK_GEAR_GUARD\x10\x0e\x12\x10\n\x0cLOCK_TONNEAU\x10\x0f*z\n\tLockState\x12\x1a\n\x16LOCK_STATE_UNSPECIFIED\x10\x00\x12\x15\n\x11LOCK_STATE_LOCKED\x10\x01\x12\x17\n\x13LOCK_STATE_UNLOCKED\x10\x02\x12!\n\x1dLOCK_STATE_PARTIALLY_UNLOCKED\x10\x03*?\n\tLockFault\x12\x1a\n\x16LOCK_FAULT_UNSPECIFIED\x10\x00\x12\x16\n\x12LOCK_FAULT_FAULTED\x10\x01*\xcb\x01\n\x0eWindowInstance\x12\x1f\n\x1bWINDOW_INSTANCE_UNSPECIFIED\x10\x00\x12\x1e\n\x1aWINDOW_INSTANCE_FRONT_LEFT\x10\x01\x12\x1f\n\x1bWINDOW_INSTANCE_FRONT_RIGHT\x10\x02\x12\x1d\n\x19WINDOW_INSTANCE_REAR_LEFT\x10\x03\x12\x1e\n\x1aWINDOW_INSTANCE_REAR_RIGHT\x10\x04\x12\x18\n\x14WINDOW_INSTANCE_REAR\x10\x05*\x81\x01\n\x11\x43\x61librationStatus\x12\"\n\x1e\x43\x41LIBRATION_STATUS_UNSPECIFIED\x10\x00\x12!\n\x1d\x43\x41LIBRATION_STATUS_CALIBRATED\x10\x01\x12%\n!CALIBRATION_STATUS_NOT_CALIBRATED\x10\x02*\xd8\x01\n\x0fRearHitchStatus\x12!\n\x1dREAR_HITCH_STATUS_UNSPECIFIED\x10\x00\x12!\n\x1dREAR_HITCH_STATUS_NOT_PRESENT\x10\x01\x12\x1f\n\x1bREAR_HITCH_STATUS_ACCESSORY\x10\x02\x12\x1e\n\x1aREAR_HITCH_STATUS_TRAILER1\x10\x03\x12\x1e\n\x1aREAR_HITCH_STATUS_TRAILER2\x10\x04\x12\x1e\n\x1aREAR_HITCH_STATUS_TRAILER3\x10\x05*\xaa\x01\n\x0c\x43losureFault\x12\x1d\n\x19\x43LOSURE_FAULT_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43LOSURE_FAULT_NO_FAULT\x10\x01\x12\x19\n\x15\x43LOSURE_FAULT_GENERAL\x10\x02\x12&\n\"CLOSURE_FAULT_NO_POWERED_OPERATION\x10\x03\x12\x1c\n\x18\x43LOSURE_FAULT_OBSTRUCTED\x10\x04*\x97\x01\n\x0fTrailerPresence\x12 \n\x1cTRAILER_PRESENCE_UNSPECIFIED\x10\x00\x12\x17\n\x13TRAILER_NOT_PRESENT\x10\x01\x12\x13\n\x0fTRAILER_PRESENT\x10\x02\x12\x1f\n\x1bTRAILER_PRESENT_WITH_BRAKES\x10\x03\x12\x13\n\x0fTRAILER_INVALID\x10\x04*\x9f\x04\n\x18\x43hargePortDoorNextAction\x12\x18\n\x14\x43HARGE_PORT_DOOR_SNA\x10\x00\x12!\n\x1d\x43HARGE_PORT_DOOR_OPEN_ALLOWED\x10\x01\x12\'\n#CHARGE_PORT_DOOR_OPEN_NOT_AVAILABLE\x10\x02\x12-\n)CHARGE_PORT_DOOR_OPEN_NOT_ALLOWED_FAULTED\x10\x03\x12\x1c\n\x18\x43HARGE_PORT_DOOR_OPENING\x10\x04\x12\"\n\x1e\x43HARGE_PORT_DOOR_CLOSE_ALLOWED\x10\x05\x12;\n7CHARGE_PORT_DOOR_OBSTRUCTED_WHILE_OPENING_CLOSE_ALLOWED\x10\x06\x12:\n6CHARGE_PORT_DOOR_OBSTRUCTED_WHILE_OPENING_OPEN_ALLOWED\x10\x07\x12;\n7CHARGE_PORT_DOOR_OBSTRUCTED_WHILE_CLOSING_CLOSE_ALLOWED\x10\x08\x12(\n$CHARGE_PORT_DOOR_CLOSE_NOT_AVAILABLE\x10\t\x12.\n*CHARGE_PORT_DOOR_CLOSE_NOT_ALLOWED_FAULTED\x10\n\x12\x1c\n\x18\x43HARGE_PORT_DOOR_CLOSING\x10\x0b*\xb0\x03\n\x0f\x46runkNextAction\x12\r\n\tFRUNK_SNA\x10\x00\x12\x16\n\x12\x46RUNK_OPEN_ALLOWED\x10\x01\x12\x17\n\x13\x46RUNK_CLOSE_ALLOWED\x10\x02\x12\x11\n\rFRUNK_OPENING\x10\x03\x12\x11\n\rFRUNK_CLOSING\x10\x04\x12\x1c\n\x18\x46RUNK_OPEN_NOT_AVAILABLE\x10\x05\x12\x1d\n\x19\x46RUNK_CLOSE_NOT_AVAILABLE\x10\x06\x12\"\n\x1e\x46RUNK_OPEN_NOT_ALLOWED_FAULTED\x10\x07\x12#\n\x1f\x46RUNK_CLOSE_NOT_ALLOWED_FAULTED\x10\x08\x12)\n%FRUNK_OPEN_ALLOWED_NO_POWER_OPERATION\x10\t\x12.\n*FRUNK_CLOSE_NOT_ALLOWED_NO_POWER_OPERATION\x10\n\x12*\n&FRUNK_OBSTRUCTED_OPENING_CLOSE_ALLOWED\x10\x0b\x12*\n&FRUNK_OBSTRUCTED_CLOSING_CLOSE_ALLOWED\x10\x0c*\xa7\x06\n\x12LiftgateNextAction\x12\x10\n\x0cLIFTGATE_SNA\x10\x00\x12\x19\n\x15LIFTGATE_OPEN_ALLOWED\x10\x01\x12\x1a\n\x16LIFTGATE_CLOSE_ALLOWED\x10\x02\x12\x14\n\x10LIFTGATE_OPENING\x10\x03\x12\x14\n\x10LIFTGATE_CLOSING\x10\x04\x12\x1f\n\x1bLIFTGATE_OPEN_NOT_AVAILABLE\x10\x05\x12 \n\x1cLIFTGATE_CLOSE_NOT_AVAILABLE\x10\x06\x12%\n!LIFTGATE_OPEN_NOT_ALLOWED_FAULTED\x10\x07\x12&\n\"LIFTGATE_CLOSE_NOT_ALLOWED_FAULTED\x10\x08\x12,\n(LIFTGATE_OPEN_ALLOWED_NO_POWER_OPERATION\x10\t\x12\x31\n-LIFTGATE_CLOSE_NOT_ALLOWED_NO_POWER_OPERATION\x10\n\x12-\n)LIFTGATE_OBSTRUCTED_OPENING_CLOSE_ALLOWED\x10\x0b\x12-\n)LIFTGATE_OBSTRUCTED_CLOSING_CLOSE_ALLOWED\x10\x0c\x12.\n*LIFTGATE_LOWER_GATE_OPEN_CLOSE_NOT_ALLOWED\x10\r\x12&\n\"LIFTGATE_OPENING_PAUSE_NOT_ALLOWED\x10\x0e\x12&\n\"LIFTGATE_CLOSING_PAUSE_NOT_ALLOWED\x10\x0f\x12+\n\'LIFTGATE_OPEN_ALLOWED_OBSTACLE_DETECTED\x10\x10\x12*\n&LIFTGATE_OPEN_ALLOWED_TRAILER_DETECTED\x10\x11\x12,\n(LIFTGATE_CLOSE_ALLOWED_OBSTACLE_DETECTED\x10\x12\x12+\n\'LIFTGATE_CLOSE_ALLOWED_TRAILER_DETECTED\x10\x13\x12\x17\n\x13LIFTGATE_PROCESSING\x10\x14*\xb2\x02\n\x11SideBinNextAction\x12\x10\n\x0cSIDE_BIN_SNA\x10\x00\x12\x19\n\x15SIDE_BIN_OPEN_ALLOWED\x10\x01\x12\x14\n\x10SIDE_BIN_OPENING\x10\x02\x12\x1f\n\x1bSIDE_BIN_OPEN_NOT_AVAILABLE\x10\x03\x12%\n!SIDE_BIN_OPEN_NOT_ALLOWED_FAULTED\x10\x04\x12\x32\n.SIDE_BIN_STUCK_AJAR_WHILE_OPENING_OPEN_ALLOWED\x10\x05\x12-\n)SIDE_BIN_OPEN_ALREADY_NO_ACTION_AVAILABLE\x10\x06\x12/\n+SIDE_BIN_OPEN_ALLOWED_CONFIRM_VEHICLE_ANGLE\x10\x07*\x8c\x03\n\x12TailgateNextAction\x12\x10\n\x0cTAILGATE_SNA\x10\x00\x12\x19\n\x15TAILGATE_OPEN_ALLOWED\x10\x01\x12\x14\n\x10TAILGATE_OPENING\x10\x02\x12\x1f\n\x1bTAILGATE_OPEN_NOT_AVAILABLE\x10\x03\x12%\n!TAILGATE_OPEN_NOT_ALLOWED_FAULTED\x10\x04\x12\x32\n.TAILGATE_STUCK_AJAR_WHILE_OPENING_OPEN_ALLOWED\x10\x05\x12-\n)TAILGATE_OPEN_ALREADY_NO_ACTION_AVAILABLE\x10\x06\x12/\n+TAILGATE_OPEN_ALLOWED_CONFIRM_VEHICLE_ANGLE\x10\x07\x12+\n\'TAILGATE_OPEN_ALLOWED_OBSTACLE_DETECTED\x10\x08\x12*\n&TAILGATE_OPEN_ALLOWED_TRAILER_DETECTED\x10\t*\xb0\x03\n\x11WindowsNextAction\x12\x0f\n\x0bWINDOWS_SNA\x10\x00\x12\x18\n\x14WINDOWS_OPEN_ALLOWED\x10\x01\x12\x19\n\x15WINDOWS_CLOSE_ALLOWED\x10\x02\x12\x13\n\x0fWINDOWS_OPENING\x10\x03\x12\x13\n\x0fWINDOWS_CLOSING\x10\x04\x12\x12\n\x0eWINDOWS_MOVING\x10\x05\x12\x1e\n\x1aWINDOWS_OPEN_NOT_AVAILABLE\x10\x06\x12\x1f\n\x1bWINDOWS_CLOSE_NOT_AVAILABLE\x10\x07\x12$\n WINDOWS_OPEN_NOT_ALLOWED_FAULTED\x10\x08\x12%\n!WINDOWS_CLOSE_NOT_ALLOWED_FAULTED\x10\t\x12\x32\n.WINDOWS_OBSTRUCTED_WHILE_CLOSING_CLOSE_ALLOWED\x10\n\x12*\n&WINDOWS_CLOSE_NOT_ALLOWED_UNCALIBRATED\x10\x0b\x12)\n%WINDOWS_OPEN_NOT_ALLOWED_UNCALIBRATED\x10\x0c*\x8a\x01\n\x0fWiperFluidState\x12!\n\x1dWIPER_FLUID_STATE_UNSPECIFIED\x10\x00\x12\x1c\n\x18WIPER_FLUID_STATE_NORMAL\x10\x01\x12\x19\n\x15WIPER_FLUID_STATE_LOW\x10\x02\x12\x1b\n\x17WIPER_FLUID_STATE_EMPTY\x10\x03\x62\x06proto3')
28
28
 
29
29
  _globals = globals()
30
30
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
31
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rivian.parallax.proto.body_pb2', _globals)
32
32
  if not _descriptor._USE_C_DESCRIPTORS:
33
33
  DESCRIPTOR._loaded_options = None
34
- _globals['_CLOSUREID']._serialized_start=1512
35
- _globals['_CLOSUREID']._serialized_end=1872
36
- _globals['_CLOSURESTATE']._serialized_start=1875
37
- _globals['_CLOSURESTATE']._serialized_end=2048
38
- _globals['_LOCKID']._serialized_start=2051
39
- _globals['_LOCKID']._serialized_end=2422
40
- _globals['_LOCKSTATE']._serialized_start=2424
41
- _globals['_LOCKSTATE']._serialized_end=2546
42
- _globals['_LOCKFAULT']._serialized_start=2548
43
- _globals['_LOCKFAULT']._serialized_end=2611
44
- _globals['_WINDOWINSTANCE']._serialized_start=2614
45
- _globals['_WINDOWINSTANCE']._serialized_end=2817
46
- _globals['_CALIBRATIONSTATUS']._serialized_start=2820
47
- _globals['_CALIBRATIONSTATUS']._serialized_end=2949
48
- _globals['_REARHITCHSTATUS']._serialized_start=2952
49
- _globals['_REARHITCHSTATUS']._serialized_end=3168
50
- _globals['_CLOSUREFAULT']._serialized_start=3171
51
- _globals['_CLOSUREFAULT']._serialized_end=3341
52
- _globals['_TRAILERPRESENCE']._serialized_start=3344
53
- _globals['_TRAILERPRESENCE']._serialized_end=3495
54
- _globals['_CHARGEPORTDOORNEXTACTION']._serialized_start=3498
55
- _globals['_CHARGEPORTDOORNEXTACTION']._serialized_end=4041
56
- _globals['_FRUNKNEXTACTION']._serialized_start=4044
57
- _globals['_FRUNKNEXTACTION']._serialized_end=4476
58
- _globals['_LIFTGATENEXTACTION']._serialized_start=4479
59
- _globals['_LIFTGATENEXTACTION']._serialized_end=5286
60
- _globals['_SIDEBINNEXTACTION']._serialized_start=5289
61
- _globals['_SIDEBINNEXTACTION']._serialized_end=5595
62
- _globals['_TAILGATENEXTACTION']._serialized_start=5598
63
- _globals['_TAILGATENEXTACTION']._serialized_end=5994
64
- _globals['_WINDOWSNEXTACTION']._serialized_start=5997
65
- _globals['_WINDOWSNEXTACTION']._serialized_end=6429
66
- _globals['_WIPERFLUIDSTATE']._serialized_start=6432
67
- _globals['_WIPERFLUIDSTATE']._serialized_end=6570
34
+ _globals['_CLOSUREID']._serialized_start=1596
35
+ _globals['_CLOSUREID']._serialized_end=1956
36
+ _globals['_CLOSURESTATE']._serialized_start=1959
37
+ _globals['_CLOSURESTATE']._serialized_end=2132
38
+ _globals['_LOCKID']._serialized_start=2135
39
+ _globals['_LOCKID']._serialized_end=2506
40
+ _globals['_LOCKSTATE']._serialized_start=2508
41
+ _globals['_LOCKSTATE']._serialized_end=2630
42
+ _globals['_LOCKFAULT']._serialized_start=2632
43
+ _globals['_LOCKFAULT']._serialized_end=2695
44
+ _globals['_WINDOWINSTANCE']._serialized_start=2698
45
+ _globals['_WINDOWINSTANCE']._serialized_end=2901
46
+ _globals['_CALIBRATIONSTATUS']._serialized_start=2904
47
+ _globals['_CALIBRATIONSTATUS']._serialized_end=3033
48
+ _globals['_REARHITCHSTATUS']._serialized_start=3036
49
+ _globals['_REARHITCHSTATUS']._serialized_end=3252
50
+ _globals['_CLOSUREFAULT']._serialized_start=3255
51
+ _globals['_CLOSUREFAULT']._serialized_end=3425
52
+ _globals['_TRAILERPRESENCE']._serialized_start=3428
53
+ _globals['_TRAILERPRESENCE']._serialized_end=3579
54
+ _globals['_CHARGEPORTDOORNEXTACTION']._serialized_start=3582
55
+ _globals['_CHARGEPORTDOORNEXTACTION']._serialized_end=4125
56
+ _globals['_FRUNKNEXTACTION']._serialized_start=4128
57
+ _globals['_FRUNKNEXTACTION']._serialized_end=4560
58
+ _globals['_LIFTGATENEXTACTION']._serialized_start=4563
59
+ _globals['_LIFTGATENEXTACTION']._serialized_end=5370
60
+ _globals['_SIDEBINNEXTACTION']._serialized_start=5373
61
+ _globals['_SIDEBINNEXTACTION']._serialized_end=5679
62
+ _globals['_TAILGATENEXTACTION']._serialized_start=5682
63
+ _globals['_TAILGATENEXTACTION']._serialized_end=6078
64
+ _globals['_WINDOWSNEXTACTION']._serialized_start=6081
65
+ _globals['_WINDOWSNEXTACTION']._serialized_end=6513
66
+ _globals['_WIPERFLUIDSTATE']._serialized_start=6516
67
+ _globals['_WIPERFLUIDSTATE']._serialized_end=6654
68
68
  _globals['_CLOSURESSTATE']._serialized_start=59
69
69
  _globals['_CLOSURESSTATE']._serialized_end=836
70
70
  _globals['_CLOSURESSTATE_CLOSURE']._serialized_start=139
@@ -81,4 +81,8 @@ if not _descriptor._USE_C_DESCRIPTORS:
81
81
  _globals['_TRAILERSTATE']._serialized_end=1436
82
82
  _globals['_WIPERFLUIDLEVEL']._serialized_start=1438
83
83
  _globals['_WIPERFLUIDLEVEL']._serialized_end=1509
84
+ _globals['_UNLOCKCOMMAND']._serialized_start=1511
85
+ _globals['_UNLOCKCOMMAND']._serialized_end=1578
86
+ _globals['_LOCKCOMMAND']._serialized_start=1580
87
+ _globals['_LOCKCOMMAND']._serialized_end=1593
84
88
  # @@protoc_insertion_point(module_scope)
@@ -434,3 +434,15 @@ class WiperFluidLevel(_message.Message):
434
434
  STATE_FIELD_NUMBER: _ClassVar[int]
435
435
  state: WiperFluidState
436
436
  def __init__(self, state: _Optional[_Union[WiperFluidState, str]] = ...) -> None: ...
437
+
438
+ class UnlockCommand(_message.Message):
439
+ __slots__ = ("all", "user_preference")
440
+ ALL_FIELD_NUMBER: _ClassVar[int]
441
+ USER_PREFERENCE_FIELD_NUMBER: _ClassVar[int]
442
+ all: bool
443
+ user_preference: bool
444
+ def __init__(self, all: bool = ..., user_preference: bool = ...) -> None: ...
445
+
446
+ class LockCommand(_message.Message):
447
+ __slots__ = ()
448
+ def __init__(self) -> None: ...
@@ -0,0 +1,108 @@
1
+ syntax = "proto3";
2
+
3
+ package rivian.parallax.command;
4
+
5
+ // Reverse-engineered `sendParallaxPayload` request for a signed remote command,
6
+ // and the `vehicleCommandStatePx` message that reports on it; see ../command.py.
7
+ // Only the field numbers matter on the wire. Enum values are named as the app
8
+ // names them, except ResponseStatus, which the app names without a prefix. Unlike
9
+ // operation.proto's Operation, this one goes to the vehicle inside a signed
10
+ // Envelope.
11
+
12
+ // Wraps an Operation. Field 17 is what makes it a signed command.
13
+ message Envelope {
14
+ // The app's envelope can hold other kinds of value; a command only uses this
15
+ oneof value {
16
+ Composite composite = 10;
17
+ }
18
+ Timestamp last_update = 12;
19
+ bytes correlation_id = 16; // the command id: 16 bytes of a UUID
20
+ SecurityInfo security_info = 17;
21
+ }
22
+
23
+ message Composite {
24
+ bytes bytes_val = 2; // a serialized Operation
25
+ }
26
+
27
+ message Operation {
28
+ string rvm = 1;
29
+ OperationType type = 2;
30
+ bytes command_id = 3; // the same 16 bytes as the envelope's correlation_id
31
+ bytes payload = 4; // the RVM's message; absent when empty
32
+ Timestamp created_at = 5;
33
+ // 6 is never set by the app
34
+ string vehicle_id = 7;
35
+ }
36
+
37
+ enum OperationType {
38
+ PARALLAX_OPERATION_UNSPECIFIED = 0;
39
+ PARALLAX_OPERATION_PUBLISH = 1;
40
+ PARALLAX_OPERATION_SUBSCRIBE = 2;
41
+ PARALLAX_OPERATION_REQUEST = 3; // the only one a command uses
42
+ }
43
+
44
+ message SecurityInfo {
45
+ Signature signature = 1;
46
+ string mapped_identity = 2; // the enrolled phone's identityId for the vehicle
47
+ }
48
+
49
+ message Signature {
50
+ SignatureType type = 1;
51
+ bytes value = 2; // 32 raw bytes of the HMAC-SHA256, not hex
52
+ }
53
+
54
+ enum SignatureType {
55
+ DATA_SIGNATURE_TYPE_UNSPECIFIED = 0;
56
+ DATA_SIGNATURE_TYPE_HMAC_SHA256 = 1;
57
+ DATA_SIGNATURE_TYPE_RCS = 2;
58
+ }
59
+
60
+ message Timestamp {
61
+ int64 seconds = 1; // epoch seconds
62
+ int32 nanos = 2;
63
+ }
64
+
65
+ // What `vehicleCommandStatePx` sends, base64 encoded, as the command goes on.
66
+ message CommandState {
67
+ bytes command_id = 1; // 16 bytes of a UUID
68
+ string rvm = 2;
69
+ Progress progress = 3;
70
+ oneof result {
71
+ OperationError operation_error = 4;
72
+ // The app declares this as bytes holding a CommandResult, which is the
73
+ // same on the wire. Present, but empty, for an undefined status.
74
+ CommandResult data = 5;
75
+ }
76
+ }
77
+
78
+ enum Progress {
79
+ VEHICLE_MODEL_OP_PROGRESS_UNSPECIFIED = 0;
80
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_WAKING_UP = 1; // seen twice for one command
81
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_VALIDATING = 2;
82
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_EXECUTING = 3;
83
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_COMPLETE = 4; // the last message
84
+ }
85
+
86
+ message CommandResult {
87
+ ResponseStatus response_status = 1;
88
+ Error error = 2;
89
+ }
90
+
91
+ enum ResponseStatus {
92
+ RESPONSE_STATUS_UNDEFINED = 0;
93
+ RESPONSE_STATUS_SUCCESS = 1;
94
+ RESPONSE_STATUS_FAIL = 2;
95
+ RESPONSE_STATUS_INVALID_REQUEST = 3;
96
+ RESPONSE_STATUS_NOT_SUPPORTED = 4;
97
+ RESPONSE_STATUS_TIMEOUT = 5;
98
+ }
99
+
100
+ message Error {
101
+ int32 code = 1;
102
+ string message = 2;
103
+ }
104
+
105
+ message OperationError {
106
+ int32 error = 1; // an enum in the app, whose values were not decompiled
107
+ string message = 2;
108
+ }
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: rivian/parallax/proto/command.proto
5
+ # Protobuf Python Version: 6.31.1
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 6,
15
+ 31,
16
+ 1,
17
+ '',
18
+ 'rivian/parallax/proto/command.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+
26
+
27
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#rivian/parallax/proto/command.proto\x12\x17rivian.parallax.command\"\xdb\x01\n\x08\x45nvelope\x12\x37\n\tcomposite\x18\n \x01(\x0b\x32\".rivian.parallax.command.CompositeH\x00\x12\x37\n\x0blast_update\x18\x0c \x01(\x0b\x32\".rivian.parallax.command.Timestamp\x12\x16\n\x0e\x63orrelation_id\x18\x10 \x01(\x0c\x12<\n\rsecurity_info\x18\x11 \x01(\x0b\x32%.rivian.parallax.command.SecurityInfoB\x07\n\x05value\"\x1e\n\tComposite\x12\x11\n\tbytes_val\x18\x02 \x01(\x0c\"\xbf\x01\n\tOperation\x12\x0b\n\x03rvm\x18\x01 \x01(\t\x12\x34\n\x04type\x18\x02 \x01(\x0e\x32&.rivian.parallax.command.OperationType\x12\x12\n\ncommand_id\x18\x03 \x01(\x0c\x12\x0f\n\x07payload\x18\x04 \x01(\x0c\x12\x36\n\ncreated_at\x18\x05 \x01(\x0b\x32\".rivian.parallax.command.Timestamp\x12\x12\n\nvehicle_id\x18\x07 \x01(\t\"^\n\x0cSecurityInfo\x12\x35\n\tsignature\x18\x01 \x01(\x0b\x32\".rivian.parallax.command.Signature\x12\x17\n\x0fmapped_identity\x18\x02 \x01(\t\"P\n\tSignature\x12\x34\n\x04type\x18\x01 \x01(\x0e\x32&.rivian.parallax.command.SignatureType\x12\r\n\x05value\x18\x02 \x01(\x0c\"+\n\tTimestamp\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\"\xea\x01\n\x0c\x43ommandState\x12\x12\n\ncommand_id\x18\x01 \x01(\x0c\x12\x0b\n\x03rvm\x18\x02 \x01(\t\x12\x33\n\x08progress\x18\x03 \x01(\x0e\x32!.rivian.parallax.command.Progress\x12\x42\n\x0foperation_error\x18\x04 \x01(\x0b\x32\'.rivian.parallax.command.OperationErrorH\x00\x12\x36\n\x04\x64\x61ta\x18\x05 \x01(\x0b\x32&.rivian.parallax.command.CommandResultH\x00\x42\x08\n\x06result\"\x80\x01\n\rCommandResult\x12@\n\x0fresponse_status\x18\x01 \x01(\x0e\x32\'.rivian.parallax.command.ResponseStatus\x12-\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x1e.rivian.parallax.command.Error\"&\n\x05\x45rror\x12\x0c\n\x04\x63ode\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"0\n\x0eOperationError\x12\r\n\x05\x65rror\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t*\x95\x01\n\rOperationType\x12\"\n\x1ePARALLAX_OPERATION_UNSPECIFIED\x10\x00\x12\x1e\n\x1aPARALLAX_OPERATION_PUBLISH\x10\x01\x12 \n\x1cPARALLAX_OPERATION_SUBSCRIBE\x10\x02\x12\x1e\n\x1aPARALLAX_OPERATION_REQUEST\x10\x03*v\n\rSignatureType\x12#\n\x1f\x44\x41TA_SIGNATURE_TYPE_UNSPECIFIED\x10\x00\x12#\n\x1f\x44\x41TA_SIGNATURE_TYPE_HMAC_SHA256\x10\x01\x12\x1b\n\x17\x44\x41TA_SIGNATURE_TYPE_RCS\x10\x02*\xf9\x01\n\x08Progress\x12)\n%VEHICLE_MODEL_OP_PROGRESS_UNSPECIFIED\x10\x00\x12/\n+VEHICLE_MODEL_OP_PROGRESS_VEHICLE_WAKING_UP\x10\x01\x12\x30\n,VEHICLE_MODEL_OP_PROGRESS_VEHICLE_VALIDATING\x10\x02\x12/\n+VEHICLE_MODEL_OP_PROGRESS_VEHICLE_EXECUTING\x10\x03\x12.\n*VEHICLE_MODEL_OP_PROGRESS_VEHICLE_COMPLETE\x10\x04*\xcb\x01\n\x0eResponseStatus\x12\x1d\n\x19RESPONSE_STATUS_UNDEFINED\x10\x00\x12\x1b\n\x17RESPONSE_STATUS_SUCCESS\x10\x01\x12\x18\n\x14RESPONSE_STATUS_FAIL\x10\x02\x12#\n\x1fRESPONSE_STATUS_INVALID_REQUEST\x10\x03\x12!\n\x1dRESPONSE_STATUS_NOT_SUPPORTED\x10\x04\x12\x1b\n\x17RESPONSE_STATUS_TIMEOUT\x10\x05\x62\x06proto3')
28
+
29
+ _globals = globals()
30
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
31
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rivian.parallax.proto.command_pb2', _globals)
32
+ if not _descriptor._USE_C_DESCRIPTORS:
33
+ DESCRIPTOR._loaded_options = None
34
+ _globals['_OPERATIONTYPE']._serialized_start=1194
35
+ _globals['_OPERATIONTYPE']._serialized_end=1343
36
+ _globals['_SIGNATURETYPE']._serialized_start=1345
37
+ _globals['_SIGNATURETYPE']._serialized_end=1463
38
+ _globals['_PROGRESS']._serialized_start=1466
39
+ _globals['_PROGRESS']._serialized_end=1715
40
+ _globals['_RESPONSESTATUS']._serialized_start=1718
41
+ _globals['_RESPONSESTATUS']._serialized_end=1921
42
+ _globals['_ENVELOPE']._serialized_start=65
43
+ _globals['_ENVELOPE']._serialized_end=284
44
+ _globals['_COMPOSITE']._serialized_start=286
45
+ _globals['_COMPOSITE']._serialized_end=316
46
+ _globals['_OPERATION']._serialized_start=319
47
+ _globals['_OPERATION']._serialized_end=510
48
+ _globals['_SECURITYINFO']._serialized_start=512
49
+ _globals['_SECURITYINFO']._serialized_end=606
50
+ _globals['_SIGNATURE']._serialized_start=608
51
+ _globals['_SIGNATURE']._serialized_end=688
52
+ _globals['_TIMESTAMP']._serialized_start=690
53
+ _globals['_TIMESTAMP']._serialized_end=733
54
+ _globals['_COMMANDSTATE']._serialized_start=736
55
+ _globals['_COMMANDSTATE']._serialized_end=970
56
+ _globals['_COMMANDRESULT']._serialized_start=973
57
+ _globals['_COMMANDRESULT']._serialized_end=1101
58
+ _globals['_ERROR']._serialized_start=1103
59
+ _globals['_ERROR']._serialized_end=1141
60
+ _globals['_OPERATIONERROR']._serialized_start=1143
61
+ _globals['_OPERATIONERROR']._serialized_end=1191
62
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,151 @@
1
+ from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
2
+ from google.protobuf import descriptor as _descriptor
3
+ from google.protobuf import message as _message
4
+ from collections.abc import Mapping as _Mapping
5
+ from typing import ClassVar as _ClassVar, Optional as _Optional, Union as _Union
6
+
7
+ DESCRIPTOR: _descriptor.FileDescriptor
8
+
9
+ class OperationType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
10
+ __slots__ = ()
11
+ PARALLAX_OPERATION_UNSPECIFIED: _ClassVar[OperationType]
12
+ PARALLAX_OPERATION_PUBLISH: _ClassVar[OperationType]
13
+ PARALLAX_OPERATION_SUBSCRIBE: _ClassVar[OperationType]
14
+ PARALLAX_OPERATION_REQUEST: _ClassVar[OperationType]
15
+
16
+ class SignatureType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
17
+ __slots__ = ()
18
+ DATA_SIGNATURE_TYPE_UNSPECIFIED: _ClassVar[SignatureType]
19
+ DATA_SIGNATURE_TYPE_HMAC_SHA256: _ClassVar[SignatureType]
20
+ DATA_SIGNATURE_TYPE_RCS: _ClassVar[SignatureType]
21
+
22
+ class Progress(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
23
+ __slots__ = ()
24
+ VEHICLE_MODEL_OP_PROGRESS_UNSPECIFIED: _ClassVar[Progress]
25
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_WAKING_UP: _ClassVar[Progress]
26
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_VALIDATING: _ClassVar[Progress]
27
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_EXECUTING: _ClassVar[Progress]
28
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_COMPLETE: _ClassVar[Progress]
29
+
30
+ class ResponseStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
31
+ __slots__ = ()
32
+ RESPONSE_STATUS_UNDEFINED: _ClassVar[ResponseStatus]
33
+ RESPONSE_STATUS_SUCCESS: _ClassVar[ResponseStatus]
34
+ RESPONSE_STATUS_FAIL: _ClassVar[ResponseStatus]
35
+ RESPONSE_STATUS_INVALID_REQUEST: _ClassVar[ResponseStatus]
36
+ RESPONSE_STATUS_NOT_SUPPORTED: _ClassVar[ResponseStatus]
37
+ RESPONSE_STATUS_TIMEOUT: _ClassVar[ResponseStatus]
38
+ PARALLAX_OPERATION_UNSPECIFIED: OperationType
39
+ PARALLAX_OPERATION_PUBLISH: OperationType
40
+ PARALLAX_OPERATION_SUBSCRIBE: OperationType
41
+ PARALLAX_OPERATION_REQUEST: OperationType
42
+ DATA_SIGNATURE_TYPE_UNSPECIFIED: SignatureType
43
+ DATA_SIGNATURE_TYPE_HMAC_SHA256: SignatureType
44
+ DATA_SIGNATURE_TYPE_RCS: SignatureType
45
+ VEHICLE_MODEL_OP_PROGRESS_UNSPECIFIED: Progress
46
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_WAKING_UP: Progress
47
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_VALIDATING: Progress
48
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_EXECUTING: Progress
49
+ VEHICLE_MODEL_OP_PROGRESS_VEHICLE_COMPLETE: Progress
50
+ RESPONSE_STATUS_UNDEFINED: ResponseStatus
51
+ RESPONSE_STATUS_SUCCESS: ResponseStatus
52
+ RESPONSE_STATUS_FAIL: ResponseStatus
53
+ RESPONSE_STATUS_INVALID_REQUEST: ResponseStatus
54
+ RESPONSE_STATUS_NOT_SUPPORTED: ResponseStatus
55
+ RESPONSE_STATUS_TIMEOUT: ResponseStatus
56
+
57
+ class Envelope(_message.Message):
58
+ __slots__ = ("composite", "last_update", "correlation_id", "security_info")
59
+ COMPOSITE_FIELD_NUMBER: _ClassVar[int]
60
+ LAST_UPDATE_FIELD_NUMBER: _ClassVar[int]
61
+ CORRELATION_ID_FIELD_NUMBER: _ClassVar[int]
62
+ SECURITY_INFO_FIELD_NUMBER: _ClassVar[int]
63
+ composite: Composite
64
+ last_update: Timestamp
65
+ correlation_id: bytes
66
+ security_info: SecurityInfo
67
+ def __init__(self, composite: _Optional[_Union[Composite, _Mapping]] = ..., last_update: _Optional[_Union[Timestamp, _Mapping]] = ..., correlation_id: _Optional[bytes] = ..., security_info: _Optional[_Union[SecurityInfo, _Mapping]] = ...) -> None: ...
68
+
69
+ class Composite(_message.Message):
70
+ __slots__ = ("bytes_val",)
71
+ BYTES_VAL_FIELD_NUMBER: _ClassVar[int]
72
+ bytes_val: bytes
73
+ def __init__(self, bytes_val: _Optional[bytes] = ...) -> None: ...
74
+
75
+ class Operation(_message.Message):
76
+ __slots__ = ("rvm", "type", "command_id", "payload", "created_at", "vehicle_id")
77
+ RVM_FIELD_NUMBER: _ClassVar[int]
78
+ TYPE_FIELD_NUMBER: _ClassVar[int]
79
+ COMMAND_ID_FIELD_NUMBER: _ClassVar[int]
80
+ PAYLOAD_FIELD_NUMBER: _ClassVar[int]
81
+ CREATED_AT_FIELD_NUMBER: _ClassVar[int]
82
+ VEHICLE_ID_FIELD_NUMBER: _ClassVar[int]
83
+ rvm: str
84
+ type: OperationType
85
+ command_id: bytes
86
+ payload: bytes
87
+ created_at: Timestamp
88
+ vehicle_id: str
89
+ def __init__(self, rvm: _Optional[str] = ..., type: _Optional[_Union[OperationType, str]] = ..., command_id: _Optional[bytes] = ..., payload: _Optional[bytes] = ..., created_at: _Optional[_Union[Timestamp, _Mapping]] = ..., vehicle_id: _Optional[str] = ...) -> None: ...
90
+
91
+ class SecurityInfo(_message.Message):
92
+ __slots__ = ("signature", "mapped_identity")
93
+ SIGNATURE_FIELD_NUMBER: _ClassVar[int]
94
+ MAPPED_IDENTITY_FIELD_NUMBER: _ClassVar[int]
95
+ signature: Signature
96
+ mapped_identity: str
97
+ def __init__(self, signature: _Optional[_Union[Signature, _Mapping]] = ..., mapped_identity: _Optional[str] = ...) -> None: ...
98
+
99
+ class Signature(_message.Message):
100
+ __slots__ = ("type", "value")
101
+ TYPE_FIELD_NUMBER: _ClassVar[int]
102
+ VALUE_FIELD_NUMBER: _ClassVar[int]
103
+ type: SignatureType
104
+ value: bytes
105
+ def __init__(self, type: _Optional[_Union[SignatureType, str]] = ..., value: _Optional[bytes] = ...) -> None: ...
106
+
107
+ class Timestamp(_message.Message):
108
+ __slots__ = ("seconds", "nanos")
109
+ SECONDS_FIELD_NUMBER: _ClassVar[int]
110
+ NANOS_FIELD_NUMBER: _ClassVar[int]
111
+ seconds: int
112
+ nanos: int
113
+ def __init__(self, seconds: _Optional[int] = ..., nanos: _Optional[int] = ...) -> None: ...
114
+
115
+ class CommandState(_message.Message):
116
+ __slots__ = ("command_id", "rvm", "progress", "operation_error", "data")
117
+ COMMAND_ID_FIELD_NUMBER: _ClassVar[int]
118
+ RVM_FIELD_NUMBER: _ClassVar[int]
119
+ PROGRESS_FIELD_NUMBER: _ClassVar[int]
120
+ OPERATION_ERROR_FIELD_NUMBER: _ClassVar[int]
121
+ DATA_FIELD_NUMBER: _ClassVar[int]
122
+ command_id: bytes
123
+ rvm: str
124
+ progress: Progress
125
+ operation_error: OperationError
126
+ data: CommandResult
127
+ def __init__(self, command_id: _Optional[bytes] = ..., rvm: _Optional[str] = ..., progress: _Optional[_Union[Progress, str]] = ..., operation_error: _Optional[_Union[OperationError, _Mapping]] = ..., data: _Optional[_Union[CommandResult, _Mapping]] = ...) -> None: ...
128
+
129
+ class CommandResult(_message.Message):
130
+ __slots__ = ("response_status", "error")
131
+ RESPONSE_STATUS_FIELD_NUMBER: _ClassVar[int]
132
+ ERROR_FIELD_NUMBER: _ClassVar[int]
133
+ response_status: ResponseStatus
134
+ error: Error
135
+ def __init__(self, response_status: _Optional[_Union[ResponseStatus, str]] = ..., error: _Optional[_Union[Error, _Mapping]] = ...) -> None: ...
136
+
137
+ class Error(_message.Message):
138
+ __slots__ = ("code", "message")
139
+ CODE_FIELD_NUMBER: _ClassVar[int]
140
+ MESSAGE_FIELD_NUMBER: _ClassVar[int]
141
+ code: int
142
+ message: str
143
+ def __init__(self, code: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ...
144
+
145
+ class OperationError(_message.Message):
146
+ __slots__ = ("error", "message")
147
+ ERROR_FIELD_NUMBER: _ClassVar[int]
148
+ MESSAGE_FIELD_NUMBER: _ClassVar[int]
149
+ error: int
150
+ message: str
151
+ def __init__(self, error: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ...
rivian/rivian.py CHANGED
@@ -10,7 +10,6 @@ import uuid
10
10
  from collections.abc import Awaitable, Callable
11
11
  from datetime import date
12
12
  from typing import Any, Self
13
- from warnings import warn
14
13
 
15
14
  import aiohttp
16
15
  from aiohttp import ClientResponse, ClientWebSocketResponse
@@ -36,9 +35,10 @@ from .parallax import (
36
35
  CLIMATE_HOLD_SETTING_RVM,
37
36
  CLIMATE_HOLD_STATUS_RVM,
38
37
  encode_climate_hold_setting,
38
+ encode_command,
39
39
  encode_vehicle_operation,
40
40
  )
41
- from .utils import generate_vehicle_command_hmac
41
+ from .utils import generate_vehicle_command_hmac, send_deprecation_warning
42
42
  from .ws_monitor import WebSocketMonitor
43
43
 
44
44
  _LOGGER = logging.getLogger(__name__)
@@ -91,15 +91,14 @@ def _iso_date(value: date | str) -> str:
91
91
  return value if isinstance(value, str) else value.strftime("%Y-%m-%d")
92
92
 
93
93
 
94
- def send_deprecation_warning(old_name: str, new_name: str) -> None: # pragma: no cover
95
- """Send a deprecation warning."""
96
- message = f"{old_name} has been deprecated in favor of {new_name}, the alias will be removed in the future"
97
- warn(
98
- message,
99
- DeprecationWarning,
100
- stacklevel=2,
101
- )
102
- _LOGGER.warning(message)
94
+ def _command_id(command_id: str | None = None) -> str:
95
+ """Return a Parallax command id as a canonical UUID string, or a new one."""
96
+ if command_id is None:
97
+ return str(uuid.uuid4())
98
+ try:
99
+ return str(uuid.UUID(str(command_id)))
100
+ except ValueError as err:
101
+ raise RivianBadRequestError("A command id must be a UUID") from err
103
102
 
104
103
 
105
104
  class Rivian:
@@ -645,6 +644,27 @@ class Rivian:
645
644
  }
646
645
  return await self.__graphql_query(headers, url, graphql_json)
647
646
 
647
+ async def send_location_to_vehicle(
648
+ self, vehicle_id: str, location: str
649
+ ) -> ClientResponse:
650
+ """Send a destination to a vehicle's navigation.
651
+
652
+ `location` is an address, a place, or coordinates as "lat,lng", and Rivian
653
+ parses it. A `publishResponse.result` of 0 means the request was accepted.
654
+ """
655
+ url = GRAPHQL_GATEWAY
656
+ headers = BASE_HEADERS | {
657
+ "Csrf-Token": self._csrf_token,
658
+ "A-Sess": self._app_session_token,
659
+ "U-Sess": self._user_session_token,
660
+ }
661
+ graphql_json = {
662
+ "operationName": "parseAndShareLocationToVehicle",
663
+ "query": "mutation parseAndShareLocationToVehicle($str: String!, $vehicleId: String!) { parseAndShareLocationToVehicle(str: $str, vehicleId: $vehicleId) { __typename publishResponse { __typename result } } }",
664
+ "variables": {"str": location, "vehicleId": vehicle_id},
665
+ }
666
+ return await self.__graphql_query(headers, url, graphql_json)
667
+
648
668
  async def get_vehicle_ota_update_details(self, vehicle_id: str) -> ClientResponse:
649
669
  """Get vehicle OTA update details."""
650
670
  url = GRAPHQL_GATEWAY
@@ -684,6 +704,8 @@ class Rivian:
684
704
  VehicleCommand.CABIN_HVAC_RIGHT_SEAT_HEAT,
685
705
  VehicleCommand.CABIN_HVAC_RIGHT_SEAT_VENT,
686
706
  VehicleCommand.CABIN_HVAC_STEERING_HEAT,
707
+ VehicleCommand.CABIN_HVAC_3RD_ROW_REAR_LEFT_SEAT_HEAT,
708
+ VehicleCommand.CABIN_HVAC_3RD_ROW_REAR_RIGHT_SEAT_HEAT,
687
709
  ) and not (
688
710
  params
689
711
  and isinstance((level := params.get("level")), int)
@@ -762,6 +784,72 @@ class Rivian:
762
784
  return status.get("id")
763
785
  return None
764
786
 
787
+ async def send_parallax_command(
788
+ self,
789
+ vehicle_id: str,
790
+ rvm: str,
791
+ payload: bytes,
792
+ *,
793
+ identity_id: str,
794
+ vehicle_key: str,
795
+ private_key: str,
796
+ command_id: str | None = None,
797
+ ) -> str | None:
798
+ """Send a signed Parallax command to the vehicle, e.g. to unlock it.
799
+
800
+ `rvm` and `payload` go together, e.g. `UNLOCK_RVM` and `encode_unlock()`
801
+ from `rivian.parallax`. The keys are those of `send_vehicle_command`: a
802
+ phone key enrolled with `enroll_phone`. In testing, a sleeping vehicle
803
+ was woken by the request, so `WAKE_VEHICLE` wasn't needed first. The app
804
+ sends body commands this way only to vehicles that list `PVS_BD_CMD` as
805
+ available; how others respond is untested.
806
+
807
+ Returns the command's id, a UUID (the given `command_id`, if any), once
808
+ Rivian has accepted the command, or None if it didn't. That isn't the
809
+ command done: follow it with `subscribe_for_parallax_command_state`.
810
+
811
+ Raises `RivianBadRequestError` for a missing id or a `command_id` that
812
+ isn't a UUID, and the usual `RivianApiException` if the request fails,
813
+ which can be after Rivian has the command. Bad keys raise `ValueError`
814
+ or a `cryptography` error before anything is sent.
815
+ """
816
+ if not (vehicle_id and rvm and identity_id):
817
+ raise RivianBadRequestError(
818
+ "A Parallax command needs a vehicle id, an RVM and an identity id"
819
+ )
820
+ command_id = _command_id(command_id)
821
+ url = GRAPHQL_GATEWAY
822
+ headers = BASE_HEADERS | {
823
+ "Csrf-Token": self._csrf_token,
824
+ "A-Sess": self._app_session_token,
825
+ "U-Sess": self._user_session_token,
826
+ }
827
+ graphql_json = {
828
+ "operationName": "SendRemoteCommand",
829
+ "variables": {
830
+ "vehicleId": vehicle_id,
831
+ "model": rvm,
832
+ "parallaxPayloadB64": encode_command(
833
+ vehicle_id,
834
+ rvm,
835
+ payload,
836
+ identity_id=identity_id,
837
+ vehicle_key=vehicle_key,
838
+ private_key=private_key,
839
+ command_id=command_id,
840
+ ),
841
+ },
842
+ "query": "mutation SendRemoteCommand($vehicleId: String!, $model: String!, $parallaxPayloadB64: String!) { sendParallaxPayload(payload: $parallaxPayloadB64, meta: { vehicleId: $vehicleId model: $model isVehicleModelOp: true requiresWakeup: true } ) { success sequenceNumber } }",
843
+ }
844
+
845
+ response = await self.__graphql_query(headers, url, graphql_json)
846
+ if response.status == 200:
847
+ data = await response.json()
848
+ sent = (data.get("data") or {}).get("sendParallaxPayload") or {}
849
+ if sent.get("success"):
850
+ return command_id
851
+ return None
852
+
765
853
  async def send_vehicle_operation(
766
854
  self,
767
855
  vehicle_id: str,
@@ -889,6 +977,36 @@ class Rivian:
889
977
  payload, callback, f"{vehicle_id} {topics}"
890
978
  )
891
979
 
980
+ async def subscribe_for_parallax_command_state(
981
+ self,
982
+ vehicle_id: str,
983
+ command_id: str,
984
+ callback: Callable[[dict[str, Any]], None],
985
+ ) -> Callable[[], Awaitable[None]] | None:
986
+ """Open a web socket connection to follow a `send_parallax_command`.
987
+
988
+ The messages report the command's progress, up to its result; decode them
989
+ with `parallax.decode_command_state_message`. Subscribe before sending the
990
+ command: Rivian doesn't acknowledge a subscription, and states from before
991
+ it may not be replayed.
992
+
993
+ The last message has the progress "complete", but none comes if the
994
+ vehicle can't be reached, so keep a timeout of your own. The subscription
995
+ is kept, and resubscribed after a quiet minute or a reconnect (so a message
996
+ may come more than once), until the returned function is awaited. Returns
997
+ None if it couldn't subscribe, and raises `RivianBadRequestError` if
998
+ `command_id` isn't a UUID.
999
+ """
1000
+ command_id = _command_id(command_id)
1001
+ payload = {
1002
+ "operationName": "vehicleCommandStatePx",
1003
+ "query": "subscription vehicleCommandStatePx($vehicleId: String!, $commandId: String!) { vehicleCommandStatePx(vehicleId: $vehicleId, commandId: $commandId) { payload } }",
1004
+ "variables": {"vehicleId": vehicle_id, "commandId": command_id},
1005
+ }
1006
+ return await self._start_subscription(
1007
+ payload, callback, f"{vehicle_id} command {command_id}"
1008
+ )
1009
+
892
1010
  async def _start_subscription(
893
1011
  self,
894
1012
  payload: dict[str, Any],
rivian/utils.py CHANGED
@@ -4,14 +4,30 @@ from __future__ import annotations
4
4
 
5
5
  import hashlib
6
6
  import hmac
7
+ import logging
7
8
  from base64 import b64decode, b64encode
8
9
  from datetime import UTC, datetime
9
10
  from typing import cast
11
+ from warnings import warn
10
12
 
11
13
  from cryptography.hazmat.primitives import hashes, serialization
12
14
  from cryptography.hazmat.primitives.asymmetric import ec
13
15
  from cryptography.hazmat.primitives.kdf.hkdf import HKDF
14
16
 
17
+ _LOGGER = logging.getLogger(__name__)
18
+
19
+
20
+ def send_deprecation_warning(old_name: str, new_name: str) -> None:
21
+ """Send a deprecation warning."""
22
+ message = f"{old_name} has been deprecated in favor of {new_name}, the alias will be removed in the future"
23
+ warn(
24
+ message,
25
+ DeprecationWarning,
26
+ # Point at the code that called the deprecated function
27
+ stacklevel=3,
28
+ )
29
+ _LOGGER.warning(message)
30
+
15
31
 
16
32
  def base64_encode(data: bytes) -> str:
17
33
  """Encode bytes to Base64 string"""
@@ -69,12 +85,25 @@ def generate_key_pair() -> tuple[str, str]:
69
85
  return (public_key_str, private_key_str)
70
86
 
71
87
 
88
+ def generate_command_hmac(data: bytes, vehicle_key: str, private_key: str) -> bytes:
89
+ """Return the raw HMAC-SHA256 of `data`, keyed by the phone and vehicle keys.
90
+
91
+ Signs BLE commands and signed Parallax commands alike.
92
+ """
93
+ secret_key = get_secret_key(private_key, vehicle_key)
94
+ return bytes.fromhex(get_message_signature(secret_key, data))
95
+
96
+
97
+ # TODO: Remove around 2027-05, six months after its deprecation in 2026-11
72
98
  def generate_ble_command_hmac(
73
99
  hmac_data: bytes, vehicle_key: str, private_key: str
74
100
  ) -> bytes:
75
- """Generate ble command hmac."""
76
- secret_key = get_secret_key(private_key, vehicle_key)
77
- return bytes.fromhex(get_message_signature(secret_key, hmac_data))
101
+ """Generate ble command hmac.
102
+
103
+ Deprecated: use `generate_command_hmac`.
104
+ """
105
+ send_deprecation_warning("generate_ble_command_hmac", "generate_command_hmac")
106
+ return generate_command_hmac(hmac_data, vehicle_key, private_key)
78
107
 
79
108
 
80
109
  def generate_vehicle_command_hmac(
rivian/ws_monitor.py CHANGED
@@ -24,6 +24,8 @@ _LOGGER = logging.getLogger(__name__)
24
24
 
25
25
  RATE_LIMITED = "Rate limited"
26
26
  UNAUTHENTICATED = "Unauthenticated"
27
+ # Close code with which the server refuses a connection
28
+ FORBIDDEN = 4403
27
29
  RATE_LIMIT_BACKOFF_MIN = 30
28
30
  RATE_LIMIT_BACKOFF_MAX = 900
29
31
 
@@ -85,6 +87,8 @@ class WebSocketMonitor:
85
87
  self._backoff_reason: str | None = None
86
88
  # The server's reason for closing the current connection, if it gave one
87
89
  self._close_message: str | None = None
90
+ # Whether the server refused the current connection (4403) before its ack
91
+ self._forbidden = False
88
92
  self._backoff_state = (
89
93
  _Backoff()
90
94
  if backoff_key is None
@@ -145,8 +149,12 @@ class WebSocketMonitor:
145
149
 
146
150
  @property
147
151
  def unauthenticated(self) -> bool:
148
- """Return `True` if the server closed the latest connection as unauthenticated."""
149
- return self._close_message == UNAUTHENTICATED
152
+ """Return `True` if the server closed the latest connection as unauthenticated.
153
+
154
+ That is, with the reason "Unauthenticated" or as forbidden (4403) before
155
+ the connection was acknowledged.
156
+ """
157
+ return self._close_message == UNAUTHENTICATED or self._forbidden
150
158
 
151
159
  @property
152
160
  def backoff_remaining(self) -> float:
@@ -164,6 +172,7 @@ class WebSocketMonitor:
164
172
  self._disconnect = False
165
173
  self._backoff_reason = None
166
174
  self._close_message = None
175
+ self._forbidden = False
167
176
  self._connection_ack.clear()
168
177
  # pylint: disable=protected-access
169
178
  assert self._account._session
@@ -270,7 +279,11 @@ class WebSocketMonitor:
270
279
  if msg.type in (WSMsgType.CLOSE, WSMsgType.CLOSING, WSMsgType.CLOSED):
271
280
  self._log_message(msg)
272
281
  self._close_message = msg.extra or None
273
- if msg.extra == UNAUTHENTICATED:
282
+ # After the ack a 4403 may be transient, so reconnect instead
283
+ self._forbidden = (
284
+ not acknowledged and websocket.close_code == FORBIDDEN
285
+ )
286
+ if self.unauthenticated:
274
287
  self._disconnect = True
275
288
  elif msg.extra == RATE_LIMITED:
276
289
  self._start_backoff(BACKOFF_RATE_LIMITED)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: rivian-python-client
3
- Version: 2.3.1
3
+ Version: 2.4.0
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
@@ -65,3 +65,44 @@ editing one, regenerate its `*_pb2` modules (don't edit those by hand):
65
65
  uv run python -m grpc_tools.protoc --proto_path=src --python_out=src \
66
66
  --pyi_out=src src/rivian/parallax/proto/*.proto
67
67
  ```
68
+
69
+ ### Commands
70
+
71
+ `send_parallax_command` sends a signed command, such as unlocking
72
+ (`parallax.UNLOCK_RVM` with `parallax.encode_unlock()`; `LOCK_RVM` and
73
+ `encode_lock()` to lock), and `subscribe_for_parallax_command_state` follows it.
74
+ Subscribe first, then send:
75
+
76
+ ```python
77
+ import uuid
78
+
79
+ from rivian import parallax
80
+
81
+
82
+ def on_state(data):
83
+ if state := parallax.decode_command_state_message(data):
84
+ print(state["progress"], state["status"], state["error"])
85
+
86
+
87
+ command_id = str(uuid.uuid4())
88
+ unsubscribe = await rivian.subscribe_for_parallax_command_state(
89
+ vehicle_id, command_id, on_state
90
+ )
91
+ if unsubscribe:
92
+ await rivian.send_parallax_command(
93
+ vehicle_id,
94
+ parallax.UNLOCK_RVM,
95
+ parallax.encode_unlock(),
96
+ identity_id=identity_id,
97
+ vehicle_key=vehicle_key,
98
+ private_key=private_key,
99
+ command_id=command_id,
100
+ )
101
+ ... # wait for the progress "complete", or give up
102
+ await unsubscribe()
103
+ ```
104
+
105
+ The keys are those of `send_vehicle_command`. The command is done when the progress
106
+ is `complete`, the status `success` and there is no `error`. If you never see that,
107
+ check the vehicle before sending it again. This unlocks a real vehicle, so keep the
108
+ keys secret.
@@ -1,17 +1,18 @@
1
1
  rivian/__init__.py,sha256=m1arAfL3-WG5NQDkraIni2h-tGr-2nmltIabQetCFw8,207
2
- rivian/__version__.py,sha256=yVb3AjuZro0OqxMVJdMikqQkASaz8ZvNpOKLn4o1e30,171
3
- rivian/ble.py,sha256=isTZQHRzINhEE7_fsrP8Z3YRfvC67SN1-EeXphBa8gc,5880
4
- rivian/const.py,sha256=xMGZJILe9FqBH5b-pxrwJo5llmcKFJJDjTRBnjy7-_o,7122
2
+ rivian/__version__.py,sha256=dR-oduO2bwSixGaYt6Y_HUAhG5f_sK-djy4s6GUfu-Y,171
3
+ rivian/ble.py,sha256=ko-PBco51wqt-eJeILbXyJWOdZf87xh2uR-d3ls-4qc,5872
4
+ rivian/const.py,sha256=rl9-Dmj0E74xviYhZ9-jcv-nJ_JYrwINOygYX35ol1w,7134
5
5
  rivian/exceptions.py,sha256=JVSOTzbfbIp4pobx9mFQm12m_qgVNV2p-Zp34m590Uw,969
6
6
  rivian/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- rivian/rivian.py,sha256=wAOm4Fm_dcIWNVsApajtpW5WM5O-GCHdeU_IP_S9zBM,44161
8
- rivian/utils.py,sha256=2GeF73EF2PGEzyqEKnlaMFBcYpN2b0QgeyDsJoiyOHQ,3907
9
- rivian/ws_monitor.py,sha256=M4QqvbaDQBvV29YI_w7GF2TF4EuoqQf6KxOFVdbNyrU,15511
10
- rivian/parallax/__init__.py,sha256=8CozrZe8Z2o4SoFTGydYH0lAbuXEDB7Q0RCukzwP4L0,2535
11
- rivian/parallax/body.py,sha256=uN2b6eGrel5jn8aJzbfD30lAOy4xEf0tJpdCTRNKs2I,9604
7
+ rivian/rivian.py,sha256=H8ZH95FJU5f78MsEICw9Xt-oBbmwSWOuGAW-e85vZRU,49821
8
+ rivian/utils.py,sha256=rQ1X8ttQ5BkbfLjqE1q7teu6Lw8Gya7Xd4bb6aAwlhs,4894
9
+ rivian/ws_monitor.py,sha256=o3Cqa8b0qJj7VWnGqnebQEvtAR8xk5yS8RFld1F0u8s,16096
10
+ rivian/parallax/__init__.py,sha256=GS_hkC0FW4_Bf-xpGPUw0Y6Nd62glI2XH4JSURcxdyo,3302
11
+ rivian/parallax/body.py,sha256=F56wnpTgJO5-uXVXbtg2W-Z-XT7OPHmgxnnkwqfubqs,9996
12
12
  rivian/parallax/charging.py,sha256=pB_RtqiK9yMF3qGJdCjoqSSX3t8RMizU4mHGV6f2Y5E,17370
13
13
  rivian/parallax/comfort.py,sha256=AhRl8Hz43EQWIT6kSgWQxgZ6MEE3Yi8BpZquIPZl4yw,12682
14
- rivian/parallax/core.py,sha256=9dFAxtfNo93HO8Da1qlGknQ6G60K_r3xMFRrtwXRm1A,5737
14
+ rivian/parallax/command.py,sha256=6XAvYB5D3BsB4B5JyTEo05eAkJIGW0FkF9EzKe6BCIM,6840
15
+ rivian/parallax/core.py,sha256=VAt78QoGt8iGTzFOwWG0cTnYYyXBvPGrND1anFrLuIw,5913
15
16
  rivian/parallax/departure.py,sha256=SzgcYPG74TrkE7m4DhrspIlx2pcl7EcKowTJT7y0iDw,4244
16
17
  rivian/parallax/device_table.py,sha256=eTn3Ld-tLbil8VurrPfRWx6kwugKZ0YQPrWI6NdWi4Y,5757
17
18
  rivian/parallax/dynamics.py,sha256=F554PzA70_j32uG-8DBxsy3Lcc9QcVtTQ3N2eZbKG78,10480
@@ -30,15 +31,18 @@ rivian/parallax/user_passcodes.py,sha256=VFCCF5IwxchK1XNohk-axiDfjz5KoETzYhYqSD7
30
31
  rivian/parallax/vehicle.py,sha256=somEeK3vTC2dLiVkdXQzAC3SsVMKBU0WYTJcyL35eGM,8668
31
32
  rivian/parallax/vehicle_access.py,sha256=ZbttRDm7SdUk9rwZ2pghvhy6OTOZDbaIH0x7KjYfj48,2284
32
33
  rivian/parallax/proto/__init__.py,sha256=zK8ZbnfFPlrZtz4ehFVqCYig4fPsSO-AQ-cPo7LB3LA,331
33
- rivian/parallax/proto/body.proto,sha256=9cFj0guS_EH8InQ0SF1rG8-FVYaAcVMB360tl6dLMps,7520
34
- rivian/parallax/proto/body_pb2.py,sha256=CQuzRGDw8o3ASQtEecDGsaP-WZqOFop_nxGy468jZCE,13441
35
- rivian/parallax/proto/body_pb2.pyi,sha256=hoQvWvxLOOCydnJWJF8QgrlzaNKddl9sCo28c7SRGno,21690
34
+ rivian/parallax/proto/body.proto,sha256=sYYKDU1_tucYysXD--O5yzvIhoxgrqd9HpqUjO__i8c,7863
35
+ rivian/parallax/proto/body_pb2.py,sha256=hKy8rRpSY0CkMsB5gNBOpi0boqyeWYPHDTTjiqJVBeI,13795
36
+ rivian/parallax/proto/body_pb2.pyi,sha256=iJlK0jQ_ebplV5HZxQPCMUkq8XbZl8fMNGS2mSFwY50,22074
36
37
  rivian/parallax/proto/charging.proto,sha256=uoIcBet_ApAyDe9JFFJyLRoC3q7rxFP4t0ZRg6GrCLw,7336
37
38
  rivian/parallax/proto/charging_pb2.py,sha256=9WmzG9ns43uJfISlmDl_K67At4RIOdhn3k4jPGET_iM,12411
38
39
  rivian/parallax/proto/charging_pb2.pyi,sha256=lSDCDP4o1obDnCUXq2KYGCvPY-Oq1Pzo6qUHSzjcpxw,18820
39
40
  rivian/parallax/proto/comfort.proto,sha256=I6Lxh0FH0tkFFMeo5iV49NZfbiKViaaCb-1eTfSHJ8Y,4789
40
41
  rivian/parallax/proto/comfort_pb2.py,sha256=_RW6pCjZdWuhKQCRT4DBj-a_YNVd7d4oex_pbMDR36k,10203
41
42
  rivian/parallax/proto/comfort_pb2.pyi,sha256=BXChLWZlv3d6slTIk99pdFMtM-5Xmc8vH1ov6AnwsPg,14342
43
+ rivian/parallax/proto/command.proto,sha256=tjUvm1NQGlXfIITB9XUPQ5IVUp9q_VcFfJVoEAEjTpw,3080
44
+ rivian/parallax/proto/command_pb2.py,sha256=IJtwu6ESWSzNFcTJi62hYSlhxRqumdQ9VosFosETi_I,5450
45
+ rivian/parallax/proto/command_pb2.pyi,sha256=RXy_IHwOmgCN2YOJvbqQe8STIfcNuKBKhctGrSdFU7o,6863
42
46
  rivian/parallax/proto/departure.proto,sha256=bjt6kSNIGvW0vlMSyQVchP0jGJhSmez8E4d2orVrWww,2224
43
47
  rivian/parallax/proto/departure_pb2.py,sha256=eKBiWihlUWx3gvCTGpiuG8CepwWcSxEMJGpjC541hyw,5952
44
48
  rivian/parallax/proto/departure_pb2.pyi,sha256=Jw8vfV8PtnsK-sDbpJmiJBNiK4xgDi2TuKBTAoTeJEE,7502
@@ -93,6 +97,6 @@ rivian/parallax/proto/vehicle_pb2.pyi,sha256=uM5JxmmlviPjtUq1FFqyZ_i-ih28A46KIYU
93
97
  rivian/schemas/charging.graphql,sha256=YkaQbGtCDPtxGzmgNxOUHgYZKNDDtW7IipoaAFdGmGo,3061
94
98
  rivian/schemas/gateway.graphql,sha256=0ZJX5_bKHqLmk3rtpp9to9GwrdCxyTasMhxAPwEhsqI,17865
95
99
  rivian/schemas/orders.graphql,sha256=goM4x7gbkFHvpMf7XUv2cTkTKMaIicrT4JbIi07F_Do,4290
96
- rivian_python_client-2.3.1.dist-info/METADATA,sha256=dcHrmYLJdC68E5S6NtMYuUfGAS51c80gi88r3kE7QXI,1757
97
- rivian_python_client-2.3.1.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
98
- rivian_python_client-2.3.1.dist-info/RECORD,,
100
+ rivian_python_client-2.4.0.dist-info/METADATA,sha256=knlFQc4KJGFXkrjIXcszPc3-F5psaq7LDbCgWywnWo0,2981
101
+ rivian_python_client-2.4.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
102
+ rivian_python_client-2.4.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.4
2
+ Generator: hatchling 1.33.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any