Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions tesla_fleet_api/tesla/vehicle/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
TeslaAuthResponseAction,
SetupCloudProfileWithLocalProfileUuidAction,
GetLocalProfilesForVaultUuidAction,
BluetoothClassicPairingRequest,
BandwidthTest,
FetchKeysInfoAction,
)
from google.protobuf.timestamp_pb2 import Timestamp
from tesla_protocol.command.vehicle_pb2 import (
Expand Down Expand Up @@ -2515,3 +2518,37 @@ async def get_local_profiles_for_vault_uuid(
),
mutating=False,
)

async def bluetooth_classic_pairing_request(
self, name: str, mac_address: bytes
) -> dict[str, Any]:
"""Requests Bluetooth Classic (not BLE) pairing with a phone for calls/audio."""
return await self._sendInfotainment(
Action(
vehicleAction=VehicleAction(
bluetoothClassicPairingRequest=BluetoothClassicPairingRequest(
utf8_name=name,
mac_address=mac_address,
)
)
)
)

async def bandwidth_test(self, requested_size: int) -> dict[str, Any]:
"""Runs a diagnostic bandwidth test of the given size in bytes."""
return await self._sendInfotainment(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mark bandwidth_test as non-mutating

Because this diagnostic uses the default mutating=True, VehicleBluetooth(confirmation="optimistic") short-circuits _sendInfotainment() after the write and never waits for the BandwidthTestResponse, so the method reports success without transferring the requested response bytes; in ack/verify modes the response is still discarded because _command() has no bandwidthTestResponse branch. Pass mutating=False and return the parsed response so the method actually measures the requested size.

Useful? React with 👍 / 👎.

Action(
vehicleAction=VehicleAction(
bandwidthTest=BandwidthTest(requested_size=requested_size)
)
)
)

async def fetch_keys_info(self) -> dict[str, Any]:
"""Gets information about keys paired with the vehicle."""
return await self._sendInfotainment(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse fetch_keys_info responses

FetchKeysInfoAction is a response-bearing request: the current tesla-protocol Response oneof includes keysInfoResponse, but _command() only returns ping, vehicleData, or actionStatus before falling through to a generic success. As added here, a successful vehicle reply containing paired-key records is silently dropped, so callers never receive the information promised by fetch_keys_info(); add a parser branch and a reply-decoding test for keysInfoResponse.

Useful? React with 👍 / 👎.

Action(
vehicleAction=VehicleAction(fetchKeysInfoAction=FetchKeysInfoAction())
),
mutating=False,
)
54 changes: 54 additions & 0 deletions tests/test_ble_connectivity_diagnostics_commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""Connectivity/diagnostics commands over the mocked BLE transport."""

from tesla_protocol.command.car_server_pb2 import Action
from tesla_protocol.command.universal_message_pb2 import Domain

from ble_mocked_transport import (
MockedBleTransportTestCase,
decrypt_sent_command,
infotainment_action_ok_reply,
)


def _decode_vehicle_action(vehicle, sent_msg):
plaintext = decrypt_sent_command(vehicle, sent_msg)
action = Action.FromString(plaintext)
assert sent_msg.to_destination.domain == Domain.DOMAIN_INFOTAINMENT
return action.vehicleAction


class BluetoothClassicPairingRequestTests(MockedBleTransportTestCase):
async def test_sends_name_and_mac_address(self) -> None:
vehicle, send = self.make_vehicle()
send.return_value = infotainment_action_ok_reply()

await vehicle.bluetooth_classic_pairing_request(
"My Phone", b"\x00\x11\x22\x33\x44\x55"
)

vehicle_action = _decode_vehicle_action(vehicle, send.await_args.args[0])
req = vehicle_action.bluetoothClassicPairingRequest
self.assertEqual(req.utf8_name, "My Phone")
self.assertEqual(req.mac_address, b"\x00\x11\x22\x33\x44\x55")


class BandwidthTestTests(MockedBleTransportTestCase):
async def test_sends_requested_size(self) -> None:
vehicle, send = self.make_vehicle()
send.return_value = infotainment_action_ok_reply()

await vehicle.bandwidth_test(1024)

vehicle_action = _decode_vehicle_action(vehicle, send.await_args.args[0])
self.assertEqual(vehicle_action.bandwidthTest.requested_size, 1024)


class FetchKeysInfoTests(MockedBleTransportTestCase):
async def test_sends_fetch_keys_info_action(self) -> None:
vehicle, send = self.make_vehicle()
send.return_value = infotainment_action_ok_reply()

await vehicle.fetch_keys_info()

vehicle_action = _decode_vehicle_action(vehicle, send.await_args.args[0])
self.assertTrue(vehicle_action.HasField("fetchKeysInfoAction"))
Loading