Handle location scope in Tesla Fleet vehicle coordinator (#154731)

This commit is contained in:
Brett Adams
2025-10-18 17:28:14 +10:00
committed by Franck Nijhof
parent 03abd5d277
commit 6ac4d2dd59
3 changed files with 76 additions and 3 deletions

View File

@@ -134,7 +134,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslaFleetConfigEntry) -
api = tesla.vehicles.createSigned(vin)
else:
api = tesla.vehicles.createFleet(vin)
coordinator = TeslaFleetVehicleDataCoordinator(hass, entry, api, product)
coordinator = TeslaFleetVehicleDataCoordinator(
hass, entry, api, product, Scope.VEHICLE_LOCATION in scopes
)
await coordinator.async_config_entry_first_refresh()

View File

@@ -39,9 +39,9 @@ ENDPOINTS = [
VehicleDataEndpoint.CHARGE_STATE,
VehicleDataEndpoint.CLIMATE_STATE,
VehicleDataEndpoint.DRIVE_STATE,
VehicleDataEndpoint.LOCATION_DATA,
VehicleDataEndpoint.VEHICLE_STATE,
VehicleDataEndpoint.VEHICLE_CONFIG,
VehicleDataEndpoint.LOCATION_DATA,
]
@@ -65,6 +65,7 @@ class TeslaFleetVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
updated_once: bool
pre2021: bool
last_active: datetime
endpoints: list[VehicleDataEndpoint]
def __init__(
self,
@@ -72,6 +73,7 @@ class TeslaFleetVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
config_entry: TeslaFleetConfigEntry,
api: VehicleFleet,
product: dict,
location: bool,
) -> None:
"""Initialize TeslaFleet Vehicle Update Coordinator."""
super().__init__(
@@ -85,6 +87,11 @@ class TeslaFleetVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
self.data = flatten(product)
self.updated_once = False
self.last_active = datetime.now()
self.endpoints = (
ENDPOINTS
if location
else [ep for ep in ENDPOINTS if ep != VehicleDataEndpoint.LOCATION_DATA]
)
async def _async_update_data(self) -> dict[str, Any]:
"""Update vehicle data using TeslaFleet API."""
@@ -97,7 +104,7 @@ class TeslaFleetVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]):
if self.data["state"] != TeslaFleetState.ONLINE:
return self.data
response = await self.api.vehicle_data(endpoints=ENDPOINTS)
response = await self.api.vehicle_data(endpoints=self.endpoints)
data = response["response"]
except VehicleOffline:

View File

@@ -9,6 +9,7 @@ from aiohttp.client_exceptions import ClientResponseError
from freezegun.api import FrozenDateTimeFactory
import pytest
from syrupy.assertion import SnapshotAssertion
from tesla_fleet_api.const import Scope, VehicleDataEndpoint
from tesla_fleet_api.exceptions import (
InvalidRegion,
InvalidToken,
@@ -36,6 +37,7 @@ from homeassistant.data_entry_flow import FlowResultType
from homeassistant.helpers import device_registry as dr
from . import setup_platform
from .conftest import create_config_entry
from .const import VEHICLE_ASLEEP, VEHICLE_DATA_ALT
from tests.common import MockConfigEntry, async_fire_time_changed
@@ -497,3 +499,65 @@ async def test_bad_implementation(
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reauth_confirm"
assert not result["errors"]
async def test_vehicle_without_location_scope(
hass: HomeAssistant,
expires_at: int,
mock_vehicle_data: AsyncMock,
) -> None:
"""Test vehicle setup without VEHICLE_LOCATION scope excludes location endpoint."""
# Create config entry without VEHICLE_LOCATION scope
config_entry = create_config_entry(
expires_at,
[
Scope.OPENID,
Scope.OFFLINE_ACCESS,
Scope.VEHICLE_DEVICE_DATA,
# Deliberately exclude Scope.VEHICLE_LOCATION
],
)
await setup_platform(hass, config_entry)
assert config_entry.state is ConfigEntryState.LOADED
# Verify that vehicle_data was called without LOCATION_DATA endpoint
mock_vehicle_data.assert_called()
call_args = mock_vehicle_data.call_args
endpoints = call_args.kwargs.get("endpoints", [])
# Should not include LOCATION_DATA endpoint
assert VehicleDataEndpoint.LOCATION_DATA not in endpoints
# Should include other endpoints
assert VehicleDataEndpoint.CHARGE_STATE in endpoints
assert VehicleDataEndpoint.CLIMATE_STATE in endpoints
assert VehicleDataEndpoint.DRIVE_STATE in endpoints
assert VehicleDataEndpoint.VEHICLE_STATE in endpoints
assert VehicleDataEndpoint.VEHICLE_CONFIG in endpoints
async def test_vehicle_with_location_scope(
hass: HomeAssistant,
normal_config_entry: MockConfigEntry,
mock_vehicle_data: AsyncMock,
) -> None:
"""Test vehicle setup with VEHICLE_LOCATION scope includes location endpoint."""
await setup_platform(hass, normal_config_entry)
assert normal_config_entry.state is ConfigEntryState.LOADED
# Verify that vehicle_data was called with LOCATION_DATA endpoint
mock_vehicle_data.assert_called()
call_args = mock_vehicle_data.call_args
endpoints = call_args.kwargs.get("endpoints", [])
# Should include LOCATION_DATA endpoint when scope is present
assert VehicleDataEndpoint.LOCATION_DATA in endpoints
# Should include all other endpoints
assert VehicleDataEndpoint.CHARGE_STATE in endpoints
assert VehicleDataEndpoint.CLIMATE_STATE in endpoints
assert VehicleDataEndpoint.DRIVE_STATE in endpoints
assert VehicleDataEndpoint.VEHICLE_STATE in endpoints
assert VehicleDataEndpoint.VEHICLE_CONFIG in endpoints