From 74052a0545bd0f851cf06fb7d62a6d7065c25e92 Mon Sep 17 00:00:00 2001 From: Walt Howd Date: Sun, 8 Nov 2026 02:21:35 +0000 Subject: [PATCH] Add climate hold and pet mode commands Encoders for the payloads of the signed `comfort.cabin.set_climate_hold` and `comfort.cabin.set_pet_mode` commands, both a single on/off action as in the Rivian app: await rivian.send_parallax_command( vehicle_id, SET_PET_MODE_RVM, encode_pet_mode(True), ... ) Verified against a 2026 R1S (Gen 2) running 2026.36.01: pet mode on/off and climate hold on/off each reported the new state within seconds. The pet mode payloads match the app's (captured with mitmproxy); the app's climate hold control only sets the duration (`set_climate_hold_duration`), so that payload comes from the app's code. Not tested: Gen 1 vehicles, the R1T and R2. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01FxWHBTrHU6eTPotwMWgtJF --- src/rivian/parallax/__init__.py | 11 ++++ src/rivian/parallax/comfort.py | 14 ++++ src/rivian/parallax/proto/comfort.proto | 12 ++++ src/rivian/parallax/proto/comfort_pb2.py | 78 ++++++++++++----------- src/rivian/parallax/proto/comfort_pb2.pyi | 15 +++++ tests/parallax/test_comfort.py | 13 +++- 6 files changed, 105 insertions(+), 38 deletions(-) diff --git a/src/rivian/parallax/__init__.py b/src/rivian/parallax/__init__.py index c5ebbd4..26c5f66 100644 --- a/src/rivian/parallax/__init__.py +++ b/src/rivian/parallax/__init__.py @@ -17,6 +17,9 @@ The RVM and payload of a signed lock or unlock command. decode_command_state_message(data) -> dict | None Reads a raw `subscribe_for_parallax_command_state` message. + encode_climate_hold(on), encode_pet_mode(on) -> bytes + Payloads for the signed SET_CLIMATE_HOLD_RVM and SET_PET_MODE_RVM + commands. Layout: core.py - registry, payload parsing and shared helpers @@ -56,7 +59,11 @@ from .comfort import ( CLIMATE_HOLD_SETTING_RVM, CLIMATE_HOLD_STATUS_RVM, + SET_CLIMATE_HOLD_RVM, + SET_PET_MODE_RVM, + encode_climate_hold, encode_climate_hold_setting, + encode_pet_mode, ) from .command import decode_command_state_message, encode_command from .core import ( @@ -87,13 +94,17 @@ "CLIMATE_HOLD_STATUS_RVM", "LOCK_RVM", "PARALLAX_RVMS", + "SET_CLIMATE_HOLD_RVM", + "SET_PET_MODE_RVM", "UNLOCK_RVM", "decode_command_state_message", "decode_parallax_message", "decode_parallax_subscription_message", + "encode_climate_hold", "encode_climate_hold_setting", "encode_command", "encode_lock", + "encode_pet_mode", "encode_unlock", "encode_vehicle_operation", ] diff --git a/src/rivian/parallax/comfort.py b/src/rivian/parallax/comfort.py index 279613d..da6f64f 100644 --- a/src/rivian/parallax/comfort.py +++ b/src/rivian/parallax/comfort.py @@ -9,6 +9,8 @@ CLIMATE_HOLD_SETTING_RVM: Final = "comfort.cabin.climate_hold_setting" CLIMATE_HOLD_STATUS_RVM: Final = "comfort.cabin.climate_hold_status" +SET_CLIMATE_HOLD_RVM: Final = "comfort.cabin.set_climate_hold" +SET_PET_MODE_RVM: Final = "comfort.cabin.set_pet_mode" @RVMDecoder.register( @@ -208,6 +210,18 @@ def encode_climate_hold_setting(duration_seconds: int) -> bytes: return comfort_pb2.ClimateHoldSetting(duration=duration_seconds).SerializeToString() +def encode_climate_hold(on: bool) -> bytes: + """Encode a comfort.cabin.set_climate_hold payload.""" + action = comfort_pb2.ACTION_ON if on else comfort_pb2.ACTION_OFF + return comfort_pb2.ActionRequest(action=action).SerializeToString() + + +def encode_pet_mode(on: bool) -> bytes: + """Encode a comfort.cabin.set_pet_mode payload.""" + action = comfort_pb2.ACTION_ON if on else comfort_pb2.ACTION_OFF + return comfort_pb2.ActionRequest(action=action).SerializeToString() + + @RVMDecoder.register(CLIMATE_HOLD_SETTING_RVM, comfort_pb2.ClimateHoldSetting) def decode_climate_hold_setting(m: comfort_pb2.ClimateHoldSetting) -> dict[str, Any]: """comfort.cabin.climate_hold_setting — configured climate-hold duration. diff --git a/src/rivian/parallax/proto/comfort.proto b/src/rivian/parallax/proto/comfort.proto index 3f022f2..385ce24 100644 --- a/src/rivian/parallax/proto/comfort.proto +++ b/src/rivian/parallax/proto/comfort.proto @@ -74,6 +74,18 @@ message ClimateHoldSetting { int32 duration = 1; // seconds; empty payload when no hold is set } +// comfort.cabin.set_climate_hold and comfort.cabin.set_pet_mode; the names +// are the app's +message ActionRequest { + Action action = 1; +} + +enum Action { + ACTION_UNSPECIFIED = 0; + ACTION_ON = 1; + ACTION_OFF = 2; +} + // comfort.cabin.climate_hold_status message ClimateHoldStatus { ClimateHoldStatusValue status = 1; diff --git a/src/rivian/parallax/proto/comfort_pb2.py b/src/rivian/parallax/proto/comfort_pb2.py index d46df2b..423b48d 100644 --- a/src/rivian/parallax/proto/comfort_pb2.py +++ b/src/rivian/parallax/proto/comfort_pb2.py @@ -24,37 +24,39 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#rivian/parallax/proto/comfort.proto\x12\x17rivian.parallax.comfort\"0\n\x12HvacSettingsStatus\x12\x1a\n\x12target_temperature\x18\x01 \x01(\x02\"\xa4\x01\n\x0eUserModesState\x12\x12\n\nin_service\x18\x01 \x01(\x08\x12\x15\n\rcar_wash_mode\x18\x02 \x01(\x08\x12\x10\n\x08pet_mode\x18\x03 \x01(\x05\x12\x11\n\tcamp_mode\x18\x04 \x01(\x05\x12\x16\n\x0etransport_mode\x18\x05 \x01(\x05\x12\x14\n\x0c\x63limate_keep\x18\x06 \x01(\x05\x12\x14\n\x0c\x66\x61\x63tory_mode\x18\x07 \x01(\x05\"\x98\x01\n\x1a\x43\x61\x62inPreconditioningStatus\x12>\n\x06status\x18\x01 \x01(\x0e\x32..rivian.parallax.comfort.PreconditioningStatus\x12:\n\x04type\x18\x02 \x01(\x0e\x32,.rivian.parallax.comfort.PreconditioningType\"\xa9\x02\n\x11\x43\x61\x62inTemperatures\x12P\n\x0ctemperatures\x18\x01 \x03(\x0b\x32:.rivian.parallax.comfort.CabinTemperatures.ZoneTemperature\x12!\n\x14\x65xterior_temperature\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12\x1c\n\x14interior_temperature\x18\x03 \x01(\x02\x12\x1d\n\x10\x64river_set_point\x18\x04 \x01(\x02H\x01\x88\x01\x01\x1a\x34\n\x0fZoneTemperature\x12\x0c\n\x04seat\x18\x01 \x01(\x05\x12\x13\n\x0btemperature\x18\x02 \x01(\x02\x42\x17\n\x15_exterior_temperatureB\x13\n\x11_driver_set_point\"A\n\x17\x43\x61\x62inVentilationSetting\x12&\n\x1e\x61uto_cabin_ventilation_enabled\x18\x01 \x01(\x08\"&\n\x12\x43limateHoldSetting\x12\x10\n\x08\x64uration\x18\x01 \x01(\x05\"\xd7\x02\n\x11\x43limateHoldStatus\x12?\n\x06status\x18\x01 \x01(\x0e\x32/.rivian.parallax.comfort.ClimateHoldStatusValue\x12\x46\n\x0c\x61vailability\x18\x02 \x01(\x0e\x32\x30.rivian.parallax.comfort.ClimateHoldAvailability\x12W\n\x15unavailability_reason\x18\x03 \x01(\x0e\x32\x38.rivian.parallax.comfort.ClimateHoldUnavailabilityReason\x12\x44\n\x08\x65nd_time\x18\x04 \x01(\x0b\x32\x32.rivian.parallax.comfort.ClimateHoldStatus.EndTime\x1a\x1a\n\x07\x45ndTime\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\"L\n\x12\x44\x65\x66rostDefogStatus\x12\x36\n\x06status\x18\x01 \x01(\x0e\x32&.rivian.parallax.comfort.DefrostStatus\"\xda\x01\n\rPetModeStatus\x12\x35\n\x06status\x18\x01 \x01(\x0e\x32%.rivian.parallax.comfort.PetModeState\x12M\n\x12temperature_status\x18\x02 \x01(\x0e\x32\x31.rivian.parallax.comfort.PetModeTemperatureStatus\x12\x43\n\rcabin_climate\x18\x03 \x01(\x0e\x32,.rivian.parallax.comfort.PetModeCabinClimate\"\xa4\x02\n\x16SeatConditioningStatus\x12H\n\x07surface\x18\x01 \x03(\x0b\x32\x37.rivian.parallax.comfort.SeatConditioningStatus.Surface\x1a\xbf\x01\n\x07Surface\x12\x31\n\x02id\x18\x01 \x01(\x0e\x32%.rivian.parallax.comfort.CabinSurface\x12\x37\n\x04type\x18\x02 \x01(\x0e\x32).rivian.parallax.comfort.ConditioningType\x12>\n\x05state\x18\x03 \x01(\x0e\x32*.rivian.parallax.comfort.ConditioningLevelH\x00\x88\x01\x01\x42\x08\n\x06_state*\x85\x03\n\x15PreconditioningStatus\x12&\n\"PRECONDITIONING_STATUS_UNSPECIFIED\x10\x00\x12\x1c\n\x18PRECONDITIONING_INITIATE\x10\x01\x12\x1a\n\x16PRECONDITIONING_ACTIVE\x10\x02\x12\"\n\x1ePRECONDITIONING_ACTIVE_WARNING\x10\x03\x12%\n!PRECONDITIONING_COMPLETE_MAINTAIN\x10\x04\x12-\n)PRECONDITIONING_TIMEOUT_TEMP_NOT_ACHIEVED\x10\x05\x12!\n\x1dPRECONDITIONING_ERROR_SOC_LOW\x10\x06\x12&\n\"PRECONDITIONING_ERROR_SYSTEM_FAULT\x10\x07\x12\x1f\n\x1bPRECONDITIONING_UNAVAILABLE\x10\x08\x12$\n PRECONDITIONING_TIMEOUT_COMPLETE\x10\t*\xe4\x01\n\x13PreconditioningType\x12$\n PRECONDITIONING_TYPE_UNSPECIFIED\x10\x00\x12&\n\"PRECONDITIONING_TYPE_USER_SELECTED\x10\x01\x12*\n&PRECONDITIONING_TYPE_SCREEN_PROTECTION\x10\x02\x12\"\n\x1ePRECONDITIONING_TYPE_SCHEDULED\x10\x03\x12/\n+PRECONDITIONING_TYPE_AUTO_CABIN_VENTILATION\x10\x04*\xba\x01\n\x16\x43limateHoldStatusValue\x12#\n\x1f\x43LIMATE_HOLD_STATUS_UNSPECIFIED\x10\x00\x12#\n\x1f\x43LIMATE_HOLD_STATUS_UNAVAILABLE\x10\x01\x12\x1b\n\x17\x43LIMATE_HOLD_STATUS_OFF\x10\x02\x12\x1a\n\x16\x43LIMATE_HOLD_STATUS_ON\x10\x03\x12\x1d\n\x19\x43LIMATE_HOLD_STATUS_FAULT\x10\x04*\x9d\x01\n\x17\x43limateHoldAvailability\x12)\n%CLIMATE_HOLD_AVAILABILITY_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43LIMATE_HOLD_AVAILABLE\x10\x01\x12\x1d\n\x19\x43LIMATE_HOLD_CONTROLLABLE\x10\x02\x12\x1c\n\x18\x43LIMATE_HOLD_UNAVAILABLE\x10\x03*\xc7\x01\n\x1f\x43limateHoldUnavailabilityReason\x12+\n\'CLIMATE_HOLD_UNAVAILABILITY_UNSPECIFIED\x10\x00\x12\'\n#CLIMATE_HOLD_UNAVAILABILITY_UNKNOWN\x10\x01\x12\'\n#CLIMATE_HOLD_UNAVAILABILITY_LOW_SOC\x10\x02\x12%\n!CLIMATE_HOLD_UNAVAILABILITY_FAULT\x10\x03*\x82\x01\n\rDefrostStatus\x12\x1e\n\x1a\x44\x45\x46ROST_STATUS_UNSPECIFIED\x10\x00\x12\x11\n\rDEFROST_DEFOG\x10\x01\x12\x12\n\x0e\x44\x45\x46ROST_ACTIVE\x10\x02\x12\x19\n\x15\x44\x45\x46ROST_DEFOG_DEFROST\x10\x03\x12\x0f\n\x0b\x44\x45\x46ROST_OFF\x10\x04*~\n\x13PetModeCabinClimate\x12&\n\"PET_MODE_CABIN_CLIMATE_COMFORTABLE\x10\x00\x12\x1f\n\x1bPET_MODE_CABIN_CLIMATE_COLD\x10\x01\x12\x1e\n\x1aPET_MODE_CABIN_CLIMATE_HOT\x10\x02*]\n\x0cPetModeState\x12\x10\n\x0cPET_MODE_OFF\x10\x00\x12\x0f\n\x0bPET_MODE_ON\x10\x01\x12\x15\n\x11PET_MODE_DISABLED\x10\x02\x12\x13\n\x0fPET_MODE_FAULTY\x10\x03*\x9a\x01\n\x18PetModeTemperatureStatus\x12 \n\x1cPET_MODE_TEMPERATURE_DEFAULT\x10\x00\x12\x1d\n\x19PET_MODE_TEMPERATURE_COLD\x10\x01\x12\x1c\n\x18PET_MODE_TEMPERATURE_HOT\x10\x02\x12\x1f\n\x1bPET_MODE_TEMPERATURE_FAULTY\x10\x03*\xce\x02\n\x0c\x43\x61\x62inSurface\x12\x1d\n\x19\x43\x41\x42IN_SURFACE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTEERING_WHEEL\x10\x01\x12\x0e\n\nREAR_GLASS\x10\x02\x12\x0f\n\x0b\x46RONT_GLASS\x10\x03\x12\x14\n\x10SIDEVIEW_MIRRORS\x10\x04\x12\x13\n\x0fSEAT_ROW_1_LEFT\x10\x05\x12\x15\n\x11SEAT_ROW_1_MIDDLE\x10\x06\x12\x14\n\x10SEAT_ROW_1_RIGHT\x10\x07\x12\x13\n\x0fSEAT_ROW_2_LEFT\x10\x08\x12\x15\n\x11SEAT_ROW_2_MIDDLE\x10\t\x12\x14\n\x10SEAT_ROW_2_RIGHT\x10\n\x12\x13\n\x0fSEAT_ROW_3_LEFT\x10\x0b\x12\x15\n\x11SEAT_ROW_3_MIDDLE\x10\x0c\x12\x14\n\x10SEAT_ROW_3_RIGHT\x10\r\x12\x0e\n\nWIPER_AREA\x10\x0e*c\n\x10\x43onditioningType\x12!\n\x1d\x43ONDITIONING_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x43ONDITIONING_HEAT\x10\x01\x12\x15\n\x11\x43ONDITIONING_VENT\x10\x02*\x85\x01\n\x11\x43onditioningLevel\x12\"\n\x1e\x43ONDITIONING_LEVEL_UNSPECIFIED\x10\x00\x12\x18\n\x14\x43ONDITIONING_LEVEL_1\x10\x01\x12\x18\n\x14\x43ONDITIONING_LEVEL_2\x10\x02\x12\x18\n\x14\x43ONDITIONING_LEVEL_3\x10\x03\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#rivian/parallax/proto/comfort.proto\x12\x17rivian.parallax.comfort\"0\n\x12HvacSettingsStatus\x12\x1a\n\x12target_temperature\x18\x01 \x01(\x02\"\xa4\x01\n\x0eUserModesState\x12\x12\n\nin_service\x18\x01 \x01(\x08\x12\x15\n\rcar_wash_mode\x18\x02 \x01(\x08\x12\x10\n\x08pet_mode\x18\x03 \x01(\x05\x12\x11\n\tcamp_mode\x18\x04 \x01(\x05\x12\x16\n\x0etransport_mode\x18\x05 \x01(\x05\x12\x14\n\x0c\x63limate_keep\x18\x06 \x01(\x05\x12\x14\n\x0c\x66\x61\x63tory_mode\x18\x07 \x01(\x05\"\x98\x01\n\x1a\x43\x61\x62inPreconditioningStatus\x12>\n\x06status\x18\x01 \x01(\x0e\x32..rivian.parallax.comfort.PreconditioningStatus\x12:\n\x04type\x18\x02 \x01(\x0e\x32,.rivian.parallax.comfort.PreconditioningType\"\xa9\x02\n\x11\x43\x61\x62inTemperatures\x12P\n\x0ctemperatures\x18\x01 \x03(\x0b\x32:.rivian.parallax.comfort.CabinTemperatures.ZoneTemperature\x12!\n\x14\x65xterior_temperature\x18\x02 \x01(\x02H\x00\x88\x01\x01\x12\x1c\n\x14interior_temperature\x18\x03 \x01(\x02\x12\x1d\n\x10\x64river_set_point\x18\x04 \x01(\x02H\x01\x88\x01\x01\x1a\x34\n\x0fZoneTemperature\x12\x0c\n\x04seat\x18\x01 \x01(\x05\x12\x13\n\x0btemperature\x18\x02 \x01(\x02\x42\x17\n\x15_exterior_temperatureB\x13\n\x11_driver_set_point\"A\n\x17\x43\x61\x62inVentilationSetting\x12&\n\x1e\x61uto_cabin_ventilation_enabled\x18\x01 \x01(\x08\"&\n\x12\x43limateHoldSetting\x12\x10\n\x08\x64uration\x18\x01 \x01(\x05\"@\n\rActionRequest\x12/\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x1f.rivian.parallax.comfort.Action\"\xd7\x02\n\x11\x43limateHoldStatus\x12?\n\x06status\x18\x01 \x01(\x0e\x32/.rivian.parallax.comfort.ClimateHoldStatusValue\x12\x46\n\x0c\x61vailability\x18\x02 \x01(\x0e\x32\x30.rivian.parallax.comfort.ClimateHoldAvailability\x12W\n\x15unavailability_reason\x18\x03 \x01(\x0e\x32\x38.rivian.parallax.comfort.ClimateHoldUnavailabilityReason\x12\x44\n\x08\x65nd_time\x18\x04 \x01(\x0b\x32\x32.rivian.parallax.comfort.ClimateHoldStatus.EndTime\x1a\x1a\n\x07\x45ndTime\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\"L\n\x12\x44\x65\x66rostDefogStatus\x12\x36\n\x06status\x18\x01 \x01(\x0e\x32&.rivian.parallax.comfort.DefrostStatus\"\xda\x01\n\rPetModeStatus\x12\x35\n\x06status\x18\x01 \x01(\x0e\x32%.rivian.parallax.comfort.PetModeState\x12M\n\x12temperature_status\x18\x02 \x01(\x0e\x32\x31.rivian.parallax.comfort.PetModeTemperatureStatus\x12\x43\n\rcabin_climate\x18\x03 \x01(\x0e\x32,.rivian.parallax.comfort.PetModeCabinClimate\"\xa4\x02\n\x16SeatConditioningStatus\x12H\n\x07surface\x18\x01 \x03(\x0b\x32\x37.rivian.parallax.comfort.SeatConditioningStatus.Surface\x1a\xbf\x01\n\x07Surface\x12\x31\n\x02id\x18\x01 \x01(\x0e\x32%.rivian.parallax.comfort.CabinSurface\x12\x37\n\x04type\x18\x02 \x01(\x0e\x32).rivian.parallax.comfort.ConditioningType\x12>\n\x05state\x18\x03 \x01(\x0e\x32*.rivian.parallax.comfort.ConditioningLevelH\x00\x88\x01\x01\x42\x08\n\x06_state*\x85\x03\n\x15PreconditioningStatus\x12&\n\"PRECONDITIONING_STATUS_UNSPECIFIED\x10\x00\x12\x1c\n\x18PRECONDITIONING_INITIATE\x10\x01\x12\x1a\n\x16PRECONDITIONING_ACTIVE\x10\x02\x12\"\n\x1ePRECONDITIONING_ACTIVE_WARNING\x10\x03\x12%\n!PRECONDITIONING_COMPLETE_MAINTAIN\x10\x04\x12-\n)PRECONDITIONING_TIMEOUT_TEMP_NOT_ACHIEVED\x10\x05\x12!\n\x1dPRECONDITIONING_ERROR_SOC_LOW\x10\x06\x12&\n\"PRECONDITIONING_ERROR_SYSTEM_FAULT\x10\x07\x12\x1f\n\x1bPRECONDITIONING_UNAVAILABLE\x10\x08\x12$\n PRECONDITIONING_TIMEOUT_COMPLETE\x10\t*\xe4\x01\n\x13PreconditioningType\x12$\n PRECONDITIONING_TYPE_UNSPECIFIED\x10\x00\x12&\n\"PRECONDITIONING_TYPE_USER_SELECTED\x10\x01\x12*\n&PRECONDITIONING_TYPE_SCREEN_PROTECTION\x10\x02\x12\"\n\x1ePRECONDITIONING_TYPE_SCHEDULED\x10\x03\x12/\n+PRECONDITIONING_TYPE_AUTO_CABIN_VENTILATION\x10\x04*?\n\x06\x41\x63tion\x12\x16\n\x12\x41\x43TION_UNSPECIFIED\x10\x00\x12\r\n\tACTION_ON\x10\x01\x12\x0e\n\nACTION_OFF\x10\x02*\xba\x01\n\x16\x43limateHoldStatusValue\x12#\n\x1f\x43LIMATE_HOLD_STATUS_UNSPECIFIED\x10\x00\x12#\n\x1f\x43LIMATE_HOLD_STATUS_UNAVAILABLE\x10\x01\x12\x1b\n\x17\x43LIMATE_HOLD_STATUS_OFF\x10\x02\x12\x1a\n\x16\x43LIMATE_HOLD_STATUS_ON\x10\x03\x12\x1d\n\x19\x43LIMATE_HOLD_STATUS_FAULT\x10\x04*\x9d\x01\n\x17\x43limateHoldAvailability\x12)\n%CLIMATE_HOLD_AVAILABILITY_UNSPECIFIED\x10\x00\x12\x1a\n\x16\x43LIMATE_HOLD_AVAILABLE\x10\x01\x12\x1d\n\x19\x43LIMATE_HOLD_CONTROLLABLE\x10\x02\x12\x1c\n\x18\x43LIMATE_HOLD_UNAVAILABLE\x10\x03*\xc7\x01\n\x1f\x43limateHoldUnavailabilityReason\x12+\n\'CLIMATE_HOLD_UNAVAILABILITY_UNSPECIFIED\x10\x00\x12\'\n#CLIMATE_HOLD_UNAVAILABILITY_UNKNOWN\x10\x01\x12\'\n#CLIMATE_HOLD_UNAVAILABILITY_LOW_SOC\x10\x02\x12%\n!CLIMATE_HOLD_UNAVAILABILITY_FAULT\x10\x03*\x82\x01\n\rDefrostStatus\x12\x1e\n\x1a\x44\x45\x46ROST_STATUS_UNSPECIFIED\x10\x00\x12\x11\n\rDEFROST_DEFOG\x10\x01\x12\x12\n\x0e\x44\x45\x46ROST_ACTIVE\x10\x02\x12\x19\n\x15\x44\x45\x46ROST_DEFOG_DEFROST\x10\x03\x12\x0f\n\x0b\x44\x45\x46ROST_OFF\x10\x04*~\n\x13PetModeCabinClimate\x12&\n\"PET_MODE_CABIN_CLIMATE_COMFORTABLE\x10\x00\x12\x1f\n\x1bPET_MODE_CABIN_CLIMATE_COLD\x10\x01\x12\x1e\n\x1aPET_MODE_CABIN_CLIMATE_HOT\x10\x02*]\n\x0cPetModeState\x12\x10\n\x0cPET_MODE_OFF\x10\x00\x12\x0f\n\x0bPET_MODE_ON\x10\x01\x12\x15\n\x11PET_MODE_DISABLED\x10\x02\x12\x13\n\x0fPET_MODE_FAULTY\x10\x03*\x9a\x01\n\x18PetModeTemperatureStatus\x12 \n\x1cPET_MODE_TEMPERATURE_DEFAULT\x10\x00\x12\x1d\n\x19PET_MODE_TEMPERATURE_COLD\x10\x01\x12\x1c\n\x18PET_MODE_TEMPERATURE_HOT\x10\x02\x12\x1f\n\x1bPET_MODE_TEMPERATURE_FAULTY\x10\x03*\xce\x02\n\x0c\x43\x61\x62inSurface\x12\x1d\n\x19\x43\x41\x42IN_SURFACE_UNSPECIFIED\x10\x00\x12\x12\n\x0eSTEERING_WHEEL\x10\x01\x12\x0e\n\nREAR_GLASS\x10\x02\x12\x0f\n\x0b\x46RONT_GLASS\x10\x03\x12\x14\n\x10SIDEVIEW_MIRRORS\x10\x04\x12\x13\n\x0fSEAT_ROW_1_LEFT\x10\x05\x12\x15\n\x11SEAT_ROW_1_MIDDLE\x10\x06\x12\x14\n\x10SEAT_ROW_1_RIGHT\x10\x07\x12\x13\n\x0fSEAT_ROW_2_LEFT\x10\x08\x12\x15\n\x11SEAT_ROW_2_MIDDLE\x10\t\x12\x14\n\x10SEAT_ROW_2_RIGHT\x10\n\x12\x13\n\x0fSEAT_ROW_3_LEFT\x10\x0b\x12\x15\n\x11SEAT_ROW_3_MIDDLE\x10\x0c\x12\x14\n\x10SEAT_ROW_3_RIGHT\x10\r\x12\x0e\n\nWIPER_AREA\x10\x0e*c\n\x10\x43onditioningType\x12!\n\x1d\x43ONDITIONING_TYPE_UNSPECIFIED\x10\x00\x12\x15\n\x11\x43ONDITIONING_HEAT\x10\x01\x12\x15\n\x11\x43ONDITIONING_VENT\x10\x02*\x85\x01\n\x11\x43onditioningLevel\x12\"\n\x1e\x43ONDITIONING_LEVEL_UNSPECIFIED\x10\x00\x12\x18\n\x14\x43ONDITIONING_LEVEL_1\x10\x01\x12\x18\n\x14\x43ONDITIONING_LEVEL_2\x10\x02\x12\x18\n\x14\x43ONDITIONING_LEVEL_3\x10\x03\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rivian.parallax.proto.comfort_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: DESCRIPTOR._loaded_options = None - _globals['_PRECONDITIONINGSTATUS']._serialized_start=1784 - _globals['_PRECONDITIONINGSTATUS']._serialized_end=2173 - _globals['_PRECONDITIONINGTYPE']._serialized_start=2176 - _globals['_PRECONDITIONINGTYPE']._serialized_end=2404 - _globals['_CLIMATEHOLDSTATUSVALUE']._serialized_start=2407 - _globals['_CLIMATEHOLDSTATUSVALUE']._serialized_end=2593 - _globals['_CLIMATEHOLDAVAILABILITY']._serialized_start=2596 - _globals['_CLIMATEHOLDAVAILABILITY']._serialized_end=2753 - _globals['_CLIMATEHOLDUNAVAILABILITYREASON']._serialized_start=2756 - _globals['_CLIMATEHOLDUNAVAILABILITYREASON']._serialized_end=2955 - _globals['_DEFROSTSTATUS']._serialized_start=2958 - _globals['_DEFROSTSTATUS']._serialized_end=3088 - _globals['_PETMODECABINCLIMATE']._serialized_start=3090 - _globals['_PETMODECABINCLIMATE']._serialized_end=3216 - _globals['_PETMODESTATE']._serialized_start=3218 - _globals['_PETMODESTATE']._serialized_end=3311 - _globals['_PETMODETEMPERATURESTATUS']._serialized_start=3314 - _globals['_PETMODETEMPERATURESTATUS']._serialized_end=3468 - _globals['_CABINSURFACE']._serialized_start=3471 - _globals['_CABINSURFACE']._serialized_end=3805 - _globals['_CONDITIONINGTYPE']._serialized_start=3807 - _globals['_CONDITIONINGTYPE']._serialized_end=3906 - _globals['_CONDITIONINGLEVEL']._serialized_start=3909 - _globals['_CONDITIONINGLEVEL']._serialized_end=4042 + _globals['_PRECONDITIONINGSTATUS']._serialized_start=1850 + _globals['_PRECONDITIONINGSTATUS']._serialized_end=2239 + _globals['_PRECONDITIONINGTYPE']._serialized_start=2242 + _globals['_PRECONDITIONINGTYPE']._serialized_end=2470 + _globals['_ACTION']._serialized_start=2472 + _globals['_ACTION']._serialized_end=2535 + _globals['_CLIMATEHOLDSTATUSVALUE']._serialized_start=2538 + _globals['_CLIMATEHOLDSTATUSVALUE']._serialized_end=2724 + _globals['_CLIMATEHOLDAVAILABILITY']._serialized_start=2727 + _globals['_CLIMATEHOLDAVAILABILITY']._serialized_end=2884 + _globals['_CLIMATEHOLDUNAVAILABILITYREASON']._serialized_start=2887 + _globals['_CLIMATEHOLDUNAVAILABILITYREASON']._serialized_end=3086 + _globals['_DEFROSTSTATUS']._serialized_start=3089 + _globals['_DEFROSTSTATUS']._serialized_end=3219 + _globals['_PETMODECABINCLIMATE']._serialized_start=3221 + _globals['_PETMODECABINCLIMATE']._serialized_end=3347 + _globals['_PETMODESTATE']._serialized_start=3349 + _globals['_PETMODESTATE']._serialized_end=3442 + _globals['_PETMODETEMPERATURESTATUS']._serialized_start=3445 + _globals['_PETMODETEMPERATURESTATUS']._serialized_end=3599 + _globals['_CABINSURFACE']._serialized_start=3602 + _globals['_CABINSURFACE']._serialized_end=3936 + _globals['_CONDITIONINGTYPE']._serialized_start=3938 + _globals['_CONDITIONINGTYPE']._serialized_end=4037 + _globals['_CONDITIONINGLEVEL']._serialized_start=4040 + _globals['_CONDITIONINGLEVEL']._serialized_end=4173 _globals['_HVACSETTINGSSTATUS']._serialized_start=64 _globals['_HVACSETTINGSSTATUS']._serialized_end=112 _globals['_USERMODESSTATE']._serialized_start=115 @@ -69,16 +71,18 @@ _globals['_CABINVENTILATIONSETTING']._serialized_end=801 _globals['_CLIMATEHOLDSETTING']._serialized_start=803 _globals['_CLIMATEHOLDSETTING']._serialized_end=841 - _globals['_CLIMATEHOLDSTATUS']._serialized_start=844 - _globals['_CLIMATEHOLDSTATUS']._serialized_end=1187 - _globals['_CLIMATEHOLDSTATUS_ENDTIME']._serialized_start=1161 - _globals['_CLIMATEHOLDSTATUS_ENDTIME']._serialized_end=1187 - _globals['_DEFROSTDEFOGSTATUS']._serialized_start=1189 - _globals['_DEFROSTDEFOGSTATUS']._serialized_end=1265 - _globals['_PETMODESTATUS']._serialized_start=1268 - _globals['_PETMODESTATUS']._serialized_end=1486 - _globals['_SEATCONDITIONINGSTATUS']._serialized_start=1489 - _globals['_SEATCONDITIONINGSTATUS']._serialized_end=1781 - _globals['_SEATCONDITIONINGSTATUS_SURFACE']._serialized_start=1590 - _globals['_SEATCONDITIONINGSTATUS_SURFACE']._serialized_end=1781 + _globals['_ACTIONREQUEST']._serialized_start=843 + _globals['_ACTIONREQUEST']._serialized_end=907 + _globals['_CLIMATEHOLDSTATUS']._serialized_start=910 + _globals['_CLIMATEHOLDSTATUS']._serialized_end=1253 + _globals['_CLIMATEHOLDSTATUS_ENDTIME']._serialized_start=1227 + _globals['_CLIMATEHOLDSTATUS_ENDTIME']._serialized_end=1253 + _globals['_DEFROSTDEFOGSTATUS']._serialized_start=1255 + _globals['_DEFROSTDEFOGSTATUS']._serialized_end=1331 + _globals['_PETMODESTATUS']._serialized_start=1334 + _globals['_PETMODESTATUS']._serialized_end=1552 + _globals['_SEATCONDITIONINGSTATUS']._serialized_start=1555 + _globals['_SEATCONDITIONINGSTATUS']._serialized_end=1847 + _globals['_SEATCONDITIONINGSTATUS_SURFACE']._serialized_start=1656 + _globals['_SEATCONDITIONINGSTATUS_SURFACE']._serialized_end=1847 # @@protoc_insertion_point(module_scope) diff --git a/src/rivian/parallax/proto/comfort_pb2.pyi b/src/rivian/parallax/proto/comfort_pb2.pyi index 6e14bc4..2d24d8d 100644 --- a/src/rivian/parallax/proto/comfort_pb2.pyi +++ b/src/rivian/parallax/proto/comfort_pb2.pyi @@ -28,6 +28,12 @@ class PreconditioningType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): PRECONDITIONING_TYPE_SCHEDULED: _ClassVar[PreconditioningType] PRECONDITIONING_TYPE_AUTO_CABIN_VENTILATION: _ClassVar[PreconditioningType] +class Action(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + ACTION_UNSPECIFIED: _ClassVar[Action] + ACTION_ON: _ClassVar[Action] + ACTION_OFF: _ClassVar[Action] + class ClimateHoldStatusValue(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () CLIMATE_HOLD_STATUS_UNSPECIFIED: _ClassVar[ClimateHoldStatusValue] @@ -123,6 +129,9 @@ PRECONDITIONING_TYPE_USER_SELECTED: PreconditioningType PRECONDITIONING_TYPE_SCREEN_PROTECTION: PreconditioningType PRECONDITIONING_TYPE_SCHEDULED: PreconditioningType PRECONDITIONING_TYPE_AUTO_CABIN_VENTILATION: PreconditioningType +ACTION_UNSPECIFIED: Action +ACTION_ON: Action +ACTION_OFF: Action CLIMATE_HOLD_STATUS_UNSPECIFIED: ClimateHoldStatusValue CLIMATE_HOLD_STATUS_UNAVAILABLE: ClimateHoldStatusValue CLIMATE_HOLD_STATUS_OFF: ClimateHoldStatusValue @@ -238,6 +247,12 @@ class ClimateHoldSetting(_message.Message): duration: int def __init__(self, duration: _Optional[int] = ...) -> None: ... +class ActionRequest(_message.Message): + __slots__ = ("action",) + ACTION_FIELD_NUMBER: _ClassVar[int] + action: Action + def __init__(self, action: _Optional[_Union[Action, str]] = ...) -> None: ... + class ClimateHoldStatus(_message.Message): __slots__ = ("status", "availability", "unavailability_reason", "end_time") class EndTime(_message.Message): diff --git a/tests/parallax/test_comfort.py b/tests/parallax/test_comfort.py index aec1f47..282352f 100644 --- a/tests/parallax/test_comfort.py +++ b/tests/parallax/test_comfort.py @@ -4,7 +4,12 @@ import pytest -from rivian.parallax import CLIMATE_HOLD_SETTING_RVM, encode_climate_hold_setting +from rivian.parallax import ( + CLIMATE_HOLD_SETTING_RVM, + encode_climate_hold, + encode_climate_hold_setting, + encode_pet_mode, +) from rivian.parallax.proto import comfort_pb2 as comfort from .helpers import decode @@ -86,6 +91,12 @@ def test_climate_hold_setting() -> None: assert decode(CLIMATE_HOLD_SETTING_RVM, setting) == {"climateHoldDuration": 7200} +def test_climate_hold_and_pet_mode() -> None: + """On and off encode like the Rivian app's.""" + assert encode_climate_hold(True) == encode_pet_mode(True) == b"\x08\x01" + assert encode_climate_hold(False) == encode_pet_mode(False) == b"\x08\x02" + + def test_climate_hold_status() -> None: """The end time is read from its {seconds} wrapper.""" status = comfort.ClimateHoldStatus