Skip to content

Add signed Parallax lock/unlock commands - #229

Open
ibain wants to merge 4 commits into
bretterer:mainfrom
ibain:feat/signed-parallax-commands
Open

ibain wants to merge 4 commits into
bretterer:mainfrom
ibain:feat/signed-parallax-commands

Conversation

@ibain

@ibain ibain commented Oct 20, 2026

Copy link
Copy Markdown

This follows #228, where you suggested two pieces and new .proto files. I'm new to this repo, so please tell me if anything should be done differently. I'm happy to change it, or to split it up.

Why
The Android app (3.17.0) locks and unlocks with a signed sendParallaxPayload and reports progress on vehicleCommandStatePx. The client can do neither yet.

What

  • Rivian.send_parallax_command sends a signed command, and Rivian.subscribe_for_parallax_command_state follows it.
  • rivian.parallax gets encode_command, decode_command_state_message, encode_lock / encode_unlock and LOCK_RVM / UNLOCK_RVM.
  • A new command.proto, and lock/unlock messages in body.proto, with regenerated modules.
  • A short README example.

Testing

  • ruff, mypy and pytest pass (280 tests).
  • A golden test pins the bytes of an unlock that a real vehicle carried out (the ids are made up and the signature is injected), and the signature is checked against an independent HMAC.
  • I tried it on an R1T with app 3.17.0, from Home Assistant: unlock (including from sleep, where Rivian woke the vehicle) and lock.
  • I haven't tried a bad signature, closure commands, or how long a signed payload stays valid.

Things I wasn't sure about

  • The signature reuses generate_ble_command_hmac, since it is a raw HMAC of some bytes. I can add a separate helper if that reads better.
  • I moved the debug logging in core.py into _debug_message so the new decoder doesn't copy it. I can put it back and duplicate it if you'd rather not touch core.py.
  • The app only sends body commands this way to vehicles that list PVS_BD_CMD. I don't know how other vehicles respond, so the docs say so.
  • The keys are keyword-only, because three strings in a row are easy to swap when the command unlocks a vehicle.

It is four commits, to read in order: the schema and generated modules; core.py with command.py; the two Rivian methods; the README. If it is easier to review in two parts (codec first, then the methods), I'm glad to split it.

ibain added 4 commits October 20, 2026 09:40
command.proto describes the envelope, operation and state messages of a
signed remote command, and body.proto the lock and unlock payloads. The
modules are regenerated with the protoc hook.
encode_command signs an operation the way the Android app does,
decode_command_state_message reads the vehicleCommandStatePx messages,
and encode_lock and encode_unlock build the lock and unlock payloads.
_debug_message is taken out of core.py so the new decoder logs unknown
fields like the others.
send_parallax_command posts a signed command, and
subscribe_for_parallax_command_state follows it. The keys are
keyword-only, and a command id that is not a UUID is refused before
anything is sent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant