From 7016c19b2fbcede732d44c530a65ab991fdd93ec Mon Sep 17 00:00:00 2001 From: Brett Adams Date: Fri, 25 Apr 2025 07:59:26 +1000 Subject: [PATCH] Disable polling for modern vehicles in Teslemetry (#143495) --- .../components/teslemetry/__init__.py | 3 +++ .../components/teslemetry/coordinator.py | 5 ++++- homeassistant/components/teslemetry/entity.py | 6 +++++ homeassistant/components/teslemetry/models.py | 1 + tests/components/teslemetry/const.py | 2 ++ .../teslemetry/fixtures/products.json | 2 +- tests/components/teslemetry/test_init.py | 22 ++++++++++++++++++- 7 files changed, 38 insertions(+), 3 deletions(-) diff --git a/homeassistant/components/teslemetry/__init__.py b/homeassistant/components/teslemetry/__init__.py index d09ea66d479..9efa55de54f 100644 --- a/homeassistant/components/teslemetry/__init__.py +++ b/homeassistant/components/teslemetry/__init__.py @@ -129,12 +129,14 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) - ) firmware = vehicle_metadata[vin].get("firmware", "Unknown") stream_vehicle = stream.get_vehicle(vin) + poll = product["command_signing"] == "off" vehicles.append( TeslemetryVehicleData( api=api, config_entry=entry, coordinator=coordinator, + poll=poll, stream=stream, stream_vehicle=stream_vehicle, vin=vin, @@ -203,6 +205,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) - *( vehicle.coordinator.async_config_entry_first_refresh() for vehicle in vehicles + if vehicle.poll ), *( energysite.info_coordinator.async_config_entry_first_refresh() diff --git a/homeassistant/components/teslemetry/coordinator.py b/homeassistant/components/teslemetry/coordinator.py index 07549008a6c..406b9cb2d84 100644 --- a/homeassistant/components/teslemetry/coordinator.py +++ b/homeassistant/components/teslemetry/coordinator.py @@ -58,8 +58,11 @@ class TeslemetryVehicleDataCoordinator(DataUpdateCoordinator[dict[str, Any]]): LOGGER, config_entry=config_entry, name="Teslemetry Vehicle", - update_interval=VEHICLE_INTERVAL, ) + if product["command_signing"] == "off": + # Only allow automatic polling if its included + self.update_interval = VEHICLE_INTERVAL + self.api = api self.data = flatten(product) self.last_active = datetime.now() diff --git a/homeassistant/components/teslemetry/entity.py b/homeassistant/components/teslemetry/entity.py index 8234e552eec..9ce812980db 100644 --- a/homeassistant/components/teslemetry/entity.py +++ b/homeassistant/components/teslemetry/entity.py @@ -116,6 +116,12 @@ class TeslemetryVehicleEntity(TeslemetryEntity): self.vehicle = data self._attr_unique_id = f"{data.vin}-{key}" self._attr_device_info = data.device + + if not data.poll: + # This entities data is not available for free + # so disable it by default + self._attr_entity_registry_enabled_default = False + super().__init__(data.coordinator, key) @property diff --git a/homeassistant/components/teslemetry/models.py b/homeassistant/components/teslemetry/models.py index fd6cf12b5b9..4f0d26a1cba 100644 --- a/homeassistant/components/teslemetry/models.py +++ b/homeassistant/components/teslemetry/models.py @@ -37,6 +37,7 @@ class TeslemetryVehicleData: api: Vehicle config_entry: ConfigEntry coordinator: TeslemetryVehicleDataCoordinator + poll: bool stream: TeslemetryStream stream_vehicle: TeslemetryStreamVehicle vin: str diff --git a/tests/components/teslemetry/const.py b/tests/components/teslemetry/const.py index 31915630951..a7cd5b7a39c 100644 --- a/tests/components/teslemetry/const.py +++ b/tests/components/teslemetry/const.py @@ -11,6 +11,8 @@ WAKE_UP_ONLINE = {"response": {"state": TeslemetryState.ONLINE}, "error": None} WAKE_UP_ASLEEP = {"response": {"state": TeslemetryState.ASLEEP}, "error": None} PRODUCTS = load_json_object_fixture("products.json", DOMAIN) +PRODUCTS_MODERN = load_json_object_fixture("products.json", DOMAIN) +PRODUCTS_MODERN["response"][0]["command_signing"] = "required" VEHICLE_DATA = load_json_object_fixture("vehicle_data.json", DOMAIN) VEHICLE_DATA_ASLEEP = load_json_object_fixture("vehicle_data.json", DOMAIN) VEHICLE_DATA_ASLEEP["response"]["state"] = TeslemetryState.OFFLINE diff --git a/tests/components/teslemetry/fixtures/products.json b/tests/components/teslemetry/fixtures/products.json index 56497a6d936..f324aa96366 100644 --- a/tests/components/teslemetry/fixtures/products.json +++ b/tests/components/teslemetry/fixtures/products.json @@ -67,7 +67,7 @@ "webcam_supported": true, "wheel_type": "Pinwheel18CapKit" }, - "command_signing": "allowed", + "command_signing": "off", "release_notes_supported": true }, { diff --git a/tests/components/teslemetry/test_init.py b/tests/components/teslemetry/test_init.py index fcf9c76c939..9d19b2bdae3 100644 --- a/tests/components/teslemetry/test_init.py +++ b/tests/components/teslemetry/test_init.py @@ -2,6 +2,7 @@ from unittest.mock import AsyncMock +from freezegun.api import FrozenDateTimeFactory import pytest from syrupy.assertion import SnapshotAssertion from tesla_fleet_api.exceptions import ( @@ -10,6 +11,7 @@ from tesla_fleet_api.exceptions import ( TeslaFleetError, ) +from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL from homeassistant.components.teslemetry.models import TeslemetryData from homeassistant.config_entries import ConfigEntryState from homeassistant.const import STATE_OFF, STATE_ON, Platform @@ -17,7 +19,7 @@ from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from . import setup_platform -from .const import VEHICLE_DATA_ALT +from .const import PRODUCTS_MODERN, VEHICLE_DATA_ALT ERRORS = [ (InvalidToken, ConfigEntryState.SETUP_ERROR), @@ -169,3 +171,21 @@ async def test_no_live_status( await setup_platform(hass) assert hass.states.get("sensor.energy_site_grid_power") is None + + +async def test_modern_no_poll( + hass: HomeAssistant, + mock_vehicle_data: AsyncMock, + mock_products: AsyncMock, + freezer: FrozenDateTimeFactory, +) -> None: + """Test that modern vehicles do not poll vehicle_data.""" + + mock_products.return_value = PRODUCTS_MODERN + entry = await setup_platform(hass) + assert entry.state is ConfigEntryState.LOADED + assert mock_vehicle_data.called is False + freezer.tick(VEHICLE_INTERVAL) + assert mock_vehicle_data.called is False + freezer.tick(VEHICLE_INTERVAL) + assert mock_vehicle_data.called is False