diff --git a/homeassistant/components/tesla_fleet/__init__.py b/homeassistant/components/tesla_fleet/__init__.py index 8cf5f8b2b58..db82251f5bb 100644 --- a/homeassistant/components/tesla_fleet/__init__.py +++ b/homeassistant/components/tesla_fleet/__init__.py @@ -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() diff --git a/homeassistant/components/tesla_fleet/coordinator.py b/homeassistant/components/tesla_fleet/coordinator.py index 2d6fbf582ca..59d1c250703 100644 --- a/homeassistant/components/tesla_fleet/coordinator.py +++ b/homeassistant/components/tesla_fleet/coordinator.py @@ -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: diff --git a/tests/components/tesla_fleet/test_init.py b/tests/components/tesla_fleet/test_init.py index 3645a0f434d..02f1ca940a6 100644 --- a/tests/components/tesla_fleet/test_init.py +++ b/tests/components/tesla_fleet/test_init.py @@ -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