Skip to content

Commit 59f213d

Browse files
authored
build(tesla): bump tesla-protocol floor to >=1.4.0 for protobuf 7 support (#140)
* Bump tesla-protocol floor to >=1.4.0 for protobuf 7 compatibility tesla-protocol 1.4.0 declares protobuf>=6.32.0,<8, unblocking dependents that need to move to protobuf 7 (Home Assistant's protobuf pin included). The proto bump renamed NavigationSuperchargerRequest.order to remote_nav_trip_order and added four new GetVehicleData substates (vehicle_config, soh_state, vehicle_detail_state, display_state), now wrapped as BLE readers; a fifth (getLegacyVehicleState) is allowlisted pending live verification of how it differs from the existing legacy_vehicle_state() reader. Version bumped to 1.12.1 per the repo's dependency-widening convention. * no-mistakes(review): Add behavioral tests for 4 new BLE readers; fix stale AGENTS.md protocol-floor note * no-mistakes(document): Fix AGENTS.md unwrapped-fields list; format new BLE reader test file
1 parent 4b8cfc5 commit 59f213d

10 files changed

Lines changed: 142 additions & 16 deletions

‎AGENTS.md‎

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

‎docs/bluetooth_vehicles.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,10 @@ Available BLE state readers:
484484
- `gui_settings()`
485485
- `parked_accessory_state()`
486486
- `legacy_vehicle_state()` (CarServer's `GetVehicleState` - a different message from `vehicle_state()` above)
487+
- `vehicle_config()`
488+
- `soh_state()`
489+
- `vehicle_detail_state()`
490+
- `display_state()`
487491
- `alert_state()`
488492
- `light_show_state()`
489493
- `suspension_state()`

‎pyproject.toml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ requires = ["setuptools>=77.0"]
44

55
[project]
66
name = "tesla_fleet_api"
7-
version = "1.12.0"
7+
version = "1.12.1"
88
license = "Apache-2.0"
99
description = "Tesla Fleet API library for Python"
1010
readme = "README.md"
@@ -22,7 +22,7 @@ dependencies = [
2222
"aiolimiter>=1",
2323
"cryptography>=43",
2424
"protobuf>=6.32.0",
25-
"tesla-protocol>=0.5.0",
25+
"tesla-protocol>=1.4.0",
2626
"bleak>=0.22",
2727
"bleak-retry-connector>=3.9",
2828
]

‎tesla_fleet_api/__init__.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Tesla Fleet API"""
22

33
__author__ = "hello@teslemetry.com"
4-
__version__ = "1.12.0"
4+
__version__ = "1.12.1"
55

66
from tesla_fleet_api.const import Region, is_valid_region
77
from tesla_fleet_api.funnel import (

‎tesla_fleet_api/tesla/vehicle/bluetooth.py‎

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
GetChildPresenceDetectionState,
4545
GetClimateState,
4646
GetClosuresState,
47+
GetDisplayState,
4748
GetDriveState,
4849
GetGuiSettings,
4950
GetLightShowState,
@@ -53,10 +54,13 @@
5354
GetParentalControlsState,
5455
GetParkedAccessoryState,
5556
GetPreconditioningScheduleState,
57+
GetSohState,
5658
GetSoftwareUpdateState,
5759
GetSuspensionState,
5860
GetTirePressureState,
61+
GetVehicleConfig,
5962
GetVehicleData,
63+
GetVehicleDetailState,
6064
GetVehicleState,
6165
VehicleAction,
6266
)
@@ -89,6 +93,7 @@
8993
ChildPresenceDetectionState,
9094
ClimateState,
9195
ClosuresState,
96+
DisplayState,
9297
DriveState,
9398
GuiSettings,
9499
LightShowState,
@@ -98,10 +103,13 @@
98103
ParentalControlsState,
99104
ParkedAccessoryState,
100105
PreconditioningScheduleState,
106+
SohState,
101107
SoftwareUpdateState,
102108
SuspensionState,
103109
TirePressureState,
110+
VehicleConfig,
104111
VehicleData,
112+
VehicleDetailState,
105113
VehicleState,
106114
)
107115

@@ -1946,6 +1954,58 @@ async def legacy_vehicle_state(self) -> VehicleState:
19461954
)
19471955
).legacy_vehicle_state
19481956

1957+
async def vehicle_config(self) -> VehicleConfig:
1958+
"""Return the vehicle's configuration/options state over BLE."""
1959+
return (
1960+
await self._getInfotainment(
1961+
Action(
1962+
vehicleAction=VehicleAction(
1963+
getVehicleData=GetVehicleData(
1964+
getVehicleConfig=GetVehicleConfig()
1965+
)
1966+
)
1967+
)
1968+
)
1969+
).vehicle_config
1970+
1971+
async def soh_state(self) -> SohState:
1972+
"""Return the current state-of-health data over BLE."""
1973+
return (
1974+
await self._getInfotainment(
1975+
Action(
1976+
vehicleAction=VehicleAction(
1977+
getVehicleData=GetVehicleData(getSohState=GetSohState())
1978+
)
1979+
)
1980+
)
1981+
).soh_state
1982+
1983+
async def vehicle_detail_state(self) -> VehicleDetailState:
1984+
"""Return additional vehicle detail state over BLE."""
1985+
return (
1986+
await self._getInfotainment(
1987+
Action(
1988+
vehicleAction=VehicleAction(
1989+
getVehicleData=GetVehicleData(
1990+
getVehicleDetailState=GetVehicleDetailState()
1991+
)
1992+
)
1993+
)
1994+
)
1995+
).vehicle_detail_state
1996+
1997+
async def display_state(self) -> DisplayState:
1998+
"""Return the current display state over BLE."""
1999+
return (
2000+
await self._getInfotainment(
2001+
Action(
2002+
vehicleAction=VehicleAction(
2003+
getVehicleData=GetVehicleData(getDisplayState=GetDisplayState())
2004+
)
2005+
)
2006+
)
2007+
).display_state
2008+
19492009
async def alert_state(self) -> AlertState:
19502010
"""Return the current active vehicle alerts over BLE."""
19512011
return (

‎tesla_fleet_api/tesla/vehicle/commands.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1444,7 +1444,7 @@ async def navigation_sc_request(self, order: int) -> dict[str, Any]:
14441444
Action(
14451445
vehicleAction=VehicleAction(
14461446
navigationSuperchargerRequest=NavigationSuperchargerRequest(
1447-
order=order
1447+
remote_nav_trip_order=order # pyright: ignore[reportArgumentType]
14481448
)
14491449
)
14501450
)

‎tests/test_ble_mocked_state_readers_new.py‎

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,26 @@
1-
"""Regression tests for the 7 new BLE vehicle-data sub-state readers.
1+
"""Regression tests for the 11 new BLE vehicle-data sub-state readers.
22
33
Completes GetVehicleData coverage alongside the existing 12 readers
44
(``test_ble_mocked_state_readers.py``): gui_settings, parked_accessory_state,
55
legacy_vehicle_state, alert_state, light_show_state, suspension_state,
6-
child_presence_detection_state.
6+
child_presence_detection_state, vehicle_config, soh_state,
7+
vehicle_detail_state, display_state.
78
"""
89

910
from tesla_fleet_api.const import BluetoothVehicleData
1011
from tesla_protocol.command.car_server_pb2 import Action
1112
from tesla_protocol.command.vehicle_pb2 import (
1213
AlertState,
1314
ChildPresenceDetectionState,
15+
DisplayState,
1416
GuiSettings,
1517
LightShowState,
1618
ParkedAccessoryState,
19+
SohState,
1720
SuspensionState,
21+
VehicleConfig,
1822
VehicleData,
23+
VehicleDetailState,
1924
VehicleState,
2025
)
2126

@@ -108,6 +113,54 @@ async def test_child_presence_detection_state(self) -> None:
108113
self.assertTrue(result.cpd_hvac_active)
109114

110115

116+
class VehicleConfigTests(MockedBleTransportTestCase):
117+
async def test_vehicle_config(self) -> None:
118+
vehicle, send = self.make_vehicle()
119+
send.return_value = infotainment_vehicle_data_reply(
120+
VehicleData(vehicle_config=VehicleConfig(has_air_suspension=True))
121+
)
122+
result = await vehicle.vehicle_config()
123+
self.assertIsInstance(result, VehicleConfig)
124+
self.assertTrue(result.has_air_suspension)
125+
126+
127+
class SohStateTests(MockedBleTransportTestCase):
128+
async def test_soh_state(self) -> None:
129+
vehicle, send = self.make_vehicle()
130+
send.return_value = infotainment_vehicle_data_reply(
131+
VehicleData(
132+
soh_state=SohState(soh_result=SohState.SohResult(soh_calibrated=True))
133+
)
134+
)
135+
result = await vehicle.soh_state()
136+
self.assertIsInstance(result, SohState)
137+
self.assertTrue(result.soh_result.soh_calibrated)
138+
139+
140+
class VehicleDetailStateTests(MockedBleTransportTestCase):
141+
async def test_vehicle_detail_state(self) -> None:
142+
vehicle, send = self.make_vehicle()
143+
send.return_value = infotainment_vehicle_data_reply(
144+
VehicleData(
145+
vehicle_detail_state=VehicleDetailState(car_version="2025.14.3")
146+
)
147+
)
148+
result = await vehicle.vehicle_detail_state()
149+
self.assertIsInstance(result, VehicleDetailState)
150+
self.assertEqual(result.car_version, "2025.14.3")
151+
152+
153+
class DisplayStateTests(MockedBleTransportTestCase):
154+
async def test_display_state(self) -> None:
155+
vehicle, send = self.make_vehicle()
156+
send.return_value = infotainment_vehicle_data_reply(
157+
VehicleData(display_state=DisplayState(displayBrightnessAuto=True))
158+
)
159+
result = await vehicle.display_state()
160+
self.assertIsInstance(result, DisplayState)
161+
self.assertTrue(result.displayBrightnessAuto)
162+
163+
111164
class VehicleDataDispatchNewEndpointsTests(MockedBleTransportTestCase):
112165
"""The 7 new sub-states must wire into ``vehicle_data()``'s endpoints dispatch."""
113166

‎tests/test_ble_nav_misc_commands.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,9 @@ async def test_sends_supercharger_order(self) -> None:
9090

9191
self.assertEqual(result, {"response": {"result": True, "reason": ""}})
9292
vehicle_action = _decode_vehicle_action(vehicle, send.await_args.args[0])
93-
self.assertEqual(vehicle_action.navigationSuperchargerRequest.order, 1)
93+
self.assertEqual(
94+
vehicle_action.navigationSuperchargerRequest.remote_nav_trip_order, 1
95+
)
9496

9597

9698
class NavigationWaypointsRequestTests(MockedBleTransportTestCase):

‎tests/test_proto_coverage_lock.py‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@
5454
ALTERNATE_PATH_VEHICLE_ACTION_FIELDS | STREAMING_SUBSCRIPTION_VEHICLE_ACTION_FIELDS
5555
)
5656

57-
KNOWN_UNWRAPPED_GET_VEHICLE_DATA_FIELDS: frozenset[str] = frozenset()
57+
# A second, newer GetVehicleData request field alongside the pre-existing
58+
# getVehicleState, both requesting an (as yet unverified live) legacy vehicle
59+
# state - already wrapped as legacy_vehicle_state() via getVehicleState.
60+
# Needs live confirmation of how it differs before wrapping a second method
61+
# for what may be the same reply data.
62+
KNOWN_UNWRAPPED_GET_VEHICLE_DATA_FIELDS: frozenset[str] = frozenset(
63+
{"getLegacyVehicleState"}
64+
)
5865

5966

6067
def _vehicle_action_fields():

‎uv.lock‎

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)