From f4ad7c3fe3ab22d344d6b3d725c48de17bb5e326 Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Wed, 21 Oct 2026 19:57:06 +1000 Subject: [PATCH] Add Bluetooth open_rear_trunk and close_rear_trunk --- docs/bluetooth_vehicles.md | 8 ++++++++ tesla_fleet_api/tesla/vehicle/bluetooth.py | 20 ++++++++++++++++++++ tests/test_ble_mocked_closures_locks.py | 12 +++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/docs/bluetooth_vehicles.md b/docs/bluetooth_vehicles.md index da6b1f2..675a95b 100644 --- a/docs/bluetooth_vehicles.md +++ b/docs/bluetooth_vehicles.md @@ -502,6 +502,14 @@ async def main(): asyncio.run(main()) ``` +## Open/Close Rear Trunk (Bluetooth Only) + +`open_rear_trunk()` and `close_rear_trunk()` send an explicit open or close +request, so the result does not depend on the trunk's current state. +`actuate_trunk(Trunk.REAR)` toggles the trunk instead. A `close_rear_trunk()` +acknowledgement means the vehicle accepted the request; check +`closures_state()` or `listen_rear_trunk()` to confirm the trunk closed. + ## Get Vehicle Data You can get data from a `VehicleBluetooth` instance using the individual state diff --git a/tesla_fleet_api/tesla/vehicle/bluetooth.py b/tesla_fleet_api/tesla/vehicle/bluetooth.py index ce6d0b8..03da1f3 100644 --- a/tesla_fleet_api/tesla/vehicle/bluetooth.py +++ b/tesla_fleet_api/tesla/vehicle/bluetooth.py @@ -1365,6 +1365,26 @@ async def close_rear_passenger_door(self) -> dict[str, Any]: ) ) + async def open_rear_trunk(self) -> dict[str, Any]: + """Opens the rear trunk; unlike actuate_trunk, never closes it.""" + return await self._sendVehicleSecurity( + UnsignedMessage( + closureMoveRequest=ClosureMoveRequest( + rearTrunk=ClosureMoveType_E.CLOSURE_MOVE_TYPE_OPEN + ) + ) + ) + + async def close_rear_trunk(self) -> dict[str, Any]: + """Closes the rear trunk; unlike actuate_trunk, never opens it.""" + return await self._sendVehicleSecurity( + UnsignedMessage( + closureMoveRequest=ClosureMoveRequest( + rearTrunk=ClosureMoveType_E.CLOSURE_MOVE_TYPE_CLOSE + ) + ) + ) + async def query_display_name(self, max_attempts: int = 5) -> str | None: """Read the device name via GATT characteristic if available""" for i in range(max_attempts): diff --git a/tests/test_ble_mocked_closures_locks.py b/tests/test_ble_mocked_closures_locks.py index 2777e43..25f4adb 100644 --- a/tests/test_ble_mocked_closures_locks.py +++ b/tests/test_ble_mocked_closures_locks.py @@ -97,7 +97,7 @@ async def test_close_sends_closure_move_close(self) -> None: class IndividualDoorTests(MockedBleTransportTestCase): - """The 8 individual door commands - each sets exactly one ``ClosureMoveRequest`` field.""" + """The 8 door and 2 rear trunk commands - each sets one ``ClosureMoveRequest`` field.""" DOORS = [ ( @@ -140,6 +140,16 @@ class IndividualDoorTests(MockedBleTransportTestCase): "rearPassengerDoor", ClosureMoveType_E.CLOSURE_MOVE_TYPE_CLOSE, ), + ( + "open_rear_trunk", + "rearTrunk", + ClosureMoveType_E.CLOSURE_MOVE_TYPE_OPEN, + ), + ( + "close_rear_trunk", + "rearTrunk", + ClosureMoveType_E.CLOSURE_MOVE_TYPE_CLOSE, + ), ] async def test_each_door_command_sets_only_its_own_field(self) -> None: