mirror of
https://github.com/home-assistant/core.git
synced 2025-07-21 20:27:08 +00:00
Update vehicle type handling in Teslemetry (#148862)
This commit is contained in:
parent
bafd342d5d
commit
84e3dac406
@ -133,7 +133,7 @@ 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"
|
||||
poll = vehicle_metadata[vin].get("polling", False)
|
||||
|
||||
vehicles.append(
|
||||
TeslemetryVehicleData(
|
||||
|
@ -542,7 +542,7 @@ async def async_setup_entry(
|
||||
for vehicle in entry.runtime_data.vehicles:
|
||||
for description in VEHICLE_DESCRIPTIONS:
|
||||
if (
|
||||
not vehicle.api.pre2021
|
||||
not vehicle.poll
|
||||
and description.streaming_listener
|
||||
and vehicle.firmware >= description.streaming_firmware
|
||||
):
|
||||
|
@ -67,7 +67,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingClimateEntity(
|
||||
vehicle, TeslemetryClimateSide.DRIVER, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.44.25"
|
||||
if vehicle.poll or vehicle.firmware < "2024.44.25"
|
||||
else TeslemetryStreamingClimateEntity(
|
||||
vehicle, TeslemetryClimateSide.DRIVER, entry.runtime_data.scopes
|
||||
)
|
||||
@ -77,7 +77,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingCabinOverheatProtectionEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.44.25"
|
||||
if vehicle.poll or vehicle.firmware < "2024.44.25"
|
||||
else TeslemetryStreamingCabinOverheatProtectionEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
|
@ -45,7 +45,7 @@ async def async_setup_entry(
|
||||
chain(
|
||||
(
|
||||
TeslemetryVehiclePollingWindowEntity(vehicle, entry.runtime_data.scopes)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
|
||||
if vehicle.poll or vehicle.firmware < "2024.26"
|
||||
else TeslemetryStreamingWindowEntity(vehicle, entry.runtime_data.scopes)
|
||||
for vehicle in entry.runtime_data.vehicles
|
||||
),
|
||||
@ -53,7 +53,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingChargePortEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.44.25"
|
||||
if vehicle.poll or vehicle.firmware < "2024.44.25"
|
||||
else TeslemetryStreamingChargePortEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
@ -63,7 +63,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingFrontTrunkEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
|
||||
if vehicle.poll or vehicle.firmware < "2024.26"
|
||||
else TeslemetryStreamingFrontTrunkEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
@ -73,7 +73,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingRearTrunkEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
|
||||
if vehicle.poll or vehicle.firmware < "2024.26"
|
||||
else TeslemetryStreamingRearTrunkEntity(
|
||||
vehicle, entry.runtime_data.scopes
|
||||
)
|
||||
@ -82,7 +82,8 @@ async def async_setup_entry(
|
||||
(
|
||||
TeslemetrySunroofEntity(vehicle, entry.runtime_data.scopes)
|
||||
for vehicle in entry.runtime_data.vehicles
|
||||
if vehicle.coordinator.data.get("vehicle_config_sun_roof_installed")
|
||||
if vehicle.poll
|
||||
and vehicle.coordinator.data.get("vehicle_config_sun_roof_installed")
|
||||
),
|
||||
)
|
||||
)
|
||||
|
@ -89,7 +89,7 @@ async def async_setup_entry(
|
||||
|
||||
for vehicle in entry.runtime_data.vehicles:
|
||||
for description in DESCRIPTIONS:
|
||||
if vehicle.api.pre2021 or vehicle.firmware < description.streaming_firmware:
|
||||
if vehicle.poll or vehicle.firmware < description.streaming_firmware:
|
||||
if description.polling_prefix:
|
||||
entities.append(
|
||||
TeslemetryVehiclePollingDeviceTrackerEntity(
|
||||
|
@ -42,7 +42,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingVehicleLockEntity(
|
||||
vehicle, Scope.VEHICLE_CMDS in entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
|
||||
if vehicle.poll or vehicle.firmware < "2024.26"
|
||||
else TeslemetryStreamingVehicleLockEntity(
|
||||
vehicle, Scope.VEHICLE_CMDS in entry.runtime_data.scopes
|
||||
)
|
||||
@ -52,7 +52,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingCableLockEntity(
|
||||
vehicle, Scope.VEHICLE_CMDS in entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
|
||||
if vehicle.poll or vehicle.firmware < "2024.26"
|
||||
else TeslemetryStreamingCableLockEntity(
|
||||
vehicle, Scope.VEHICLE_CMDS in entry.runtime_data.scopes
|
||||
)
|
||||
|
@ -53,7 +53,7 @@ async def async_setup_entry(
|
||||
|
||||
async_add_entities(
|
||||
TeslemetryVehiclePollingMediaEntity(vehicle, entry.runtime_data.scopes)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2025.2.6"
|
||||
if vehicle.poll or vehicle.firmware < "2025.2.6"
|
||||
else TeslemetryStreamingMediaEntity(vehicle, entry.runtime_data.scopes)
|
||||
for vehicle in entry.runtime_data.vehicles
|
||||
)
|
||||
|
@ -145,7 +145,7 @@ async def async_setup_entry(
|
||||
description,
|
||||
entry.runtime_data.scopes,
|
||||
)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.26"
|
||||
if vehicle.poll or vehicle.firmware < "2024.26"
|
||||
else TeslemetryStreamingNumberEntity(
|
||||
vehicle,
|
||||
description,
|
||||
|
@ -180,7 +180,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingSelectEntity(
|
||||
vehicle, description, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021
|
||||
if vehicle.poll
|
||||
or vehicle.firmware < "2024.26"
|
||||
or description.streaming_listener is None
|
||||
else TeslemetryStreamingSelectEntity(
|
||||
|
@ -1565,7 +1565,7 @@ async def async_setup_entry(
|
||||
for vehicle in entry.runtime_data.vehicles:
|
||||
for description in VEHICLE_DESCRIPTIONS:
|
||||
if (
|
||||
not vehicle.api.pre2021
|
||||
not vehicle.poll
|
||||
and description.streaming_listener
|
||||
and vehicle.firmware >= description.streaming_firmware
|
||||
):
|
||||
@ -1575,7 +1575,7 @@ async def async_setup_entry(
|
||||
|
||||
for time_description in VEHICLE_TIME_DESCRIPTIONS:
|
||||
if (
|
||||
not vehicle.api.pre2021
|
||||
not vehicle.poll
|
||||
and vehicle.firmware >= time_description.streaming_firmware
|
||||
):
|
||||
entities.append(
|
||||
|
@ -147,8 +147,7 @@ async def async_setup_entry(
|
||||
TeslemetryVehiclePollingVehicleSwitchEntity(
|
||||
vehicle, description, entry.runtime_data.scopes
|
||||
)
|
||||
if vehicle.api.pre2021
|
||||
or vehicle.firmware < description.streaming_firmware
|
||||
if vehicle.poll or vehicle.firmware < description.streaming_firmware
|
||||
else TeslemetryStreamingVehicleSwitchEntity(
|
||||
vehicle, description, entry.runtime_data.scopes
|
||||
)
|
||||
|
@ -39,7 +39,7 @@ async def async_setup_entry(
|
||||
|
||||
async_add_entities(
|
||||
TeslemetryVehiclePollingUpdateEntity(vehicle, entry.runtime_data.scopes)
|
||||
if vehicle.api.pre2021 or vehicle.firmware < "2024.44.25"
|
||||
if vehicle.poll or vehicle.firmware < "2024.44.25"
|
||||
else TeslemetryStreamingUpdateEntity(vehicle, entry.runtime_data.scopes)
|
||||
for vehicle in entry.runtime_data.vehicles
|
||||
)
|
||||
|
@ -14,6 +14,7 @@ from .const import (
|
||||
ENERGY_HISTORY,
|
||||
LIVE_STATUS,
|
||||
METADATA,
|
||||
METADATA_LEGACY,
|
||||
PRODUCTS,
|
||||
SITE_INFO,
|
||||
VEHICLE_DATA,
|
||||
@ -53,9 +54,9 @@ def mock_vehicle_data() -> Generator[AsyncMock]:
|
||||
def mock_legacy():
|
||||
"""Mock Tesla Fleet Api products method."""
|
||||
with patch(
|
||||
"tesla_fleet_api.teslemetry.Vehicle.pre2021", return_value=True
|
||||
) as mock_pre2021:
|
||||
yield mock_pre2021
|
||||
"tesla_fleet_api.teslemetry.Teslemetry.metadata", return_value=METADATA_LEGACY
|
||||
) as mock_products:
|
||||
yield mock_products
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
|
@ -37,6 +37,32 @@ COMMAND_ERRORS = (COMMAND_REASON, COMMAND_NOREASON, COMMAND_ERROR, COMMAND_NOERR
|
||||
RESPONSE_OK = {"response": {}, "error": None}
|
||||
|
||||
METADATA = {
|
||||
"uid": "abc-123",
|
||||
"region": "NA",
|
||||
"scopes": [
|
||||
"openid",
|
||||
"offline_access",
|
||||
"user_data",
|
||||
"vehicle_device_data",
|
||||
"vehicle_cmds",
|
||||
"vehicle_charging_cmds",
|
||||
"vehicle_location",
|
||||
"energy_device_data",
|
||||
"energy_cmds",
|
||||
],
|
||||
"vehicles": {
|
||||
"LRW3F7EK4NC700000": {
|
||||
"proxy": True,
|
||||
"access": True,
|
||||
"polling": False,
|
||||
"firmware": "2026.0.0",
|
||||
"discounted": False,
|
||||
"fleet_telemetry": "1.0.2",
|
||||
"name": "Home Assistant",
|
||||
}
|
||||
},
|
||||
}
|
||||
METADATA_LEGACY = {
|
||||
"uid": "abc-123",
|
||||
"region": "NA",
|
||||
"scopes": [
|
||||
@ -56,6 +82,9 @@ METADATA = {
|
||||
"access": True,
|
||||
"polling": True,
|
||||
"firmware": "2026.0.0",
|
||||
"discounted": True,
|
||||
"fleet_telemetry": "unknown",
|
||||
"name": "Home Assistant",
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -68,7 +97,10 @@ METADATA_NOSCOPE = {
|
||||
"proxy": False,
|
||||
"access": True,
|
||||
"polling": True,
|
||||
"firmware": "2024.44.25",
|
||||
"firmware": "2026.0.0",
|
||||
"discounted": True,
|
||||
"fleet_telemetry": "unknown",
|
||||
"name": "Home Assistant",
|
||||
}
|
||||
},
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -407,9 +407,8 @@
|
||||
]),
|
||||
'max_temp': 40,
|
||||
'min_temp': 30,
|
||||
'supported_features': <ClimateEntityFeature: 385>,
|
||||
'supported_features': <ClimateEntityFeature: 384>,
|
||||
'target_temp_step': 5,
|
||||
'temperature': None,
|
||||
}),
|
||||
'context': <ANY>,
|
||||
'entity_id': 'climate.test_cabin_overheat_protection',
|
||||
|
@ -23,6 +23,7 @@ async def test_binary_sensor(
|
||||
hass: HomeAssistant,
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the binary sensor entities are correct."""
|
||||
|
||||
@ -37,6 +38,7 @@ async def test_binary_sensor_refresh(
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_vehicle_data: AsyncMock,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the binary sensor entities are correct."""
|
||||
|
||||
|
@ -273,7 +273,6 @@ async def test_climate_noscope(
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_metadata: AsyncMock,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the climate entity is correct."""
|
||||
mock_metadata.return_value = METADATA_NOSCOPE
|
||||
|
@ -55,7 +55,6 @@ async def test_cover_noscope(
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_metadata: AsyncMock,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the cover entities are correct without scopes."""
|
||||
|
||||
@ -67,6 +66,7 @@ async def test_cover_noscope(
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_cover_services(
|
||||
hass: HomeAssistant,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the cover entities are correct."""
|
||||
|
||||
|
@ -49,7 +49,6 @@ async def test_device_tracker_noscope(
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_metadata: AsyncMock,
|
||||
mock_vehicle_data: AsyncMock,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the device tracker entities are correct."""
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""Test the Telemetry Diagnostics."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@ -18,6 +20,7 @@ async def test_diagnostics(
|
||||
hass_client: ClientSessionGenerator,
|
||||
snapshot: SnapshotAssertion,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Test diagnostics."""
|
||||
|
||||
|
@ -14,7 +14,13 @@ from tesla_fleet_api.exceptions import (
|
||||
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, STATE_UNKNOWN, Platform
|
||||
from homeassistant.const import (
|
||||
STATE_OFF,
|
||||
STATE_ON,
|
||||
STATE_UNAVAILABLE,
|
||||
STATE_UNKNOWN,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
@ -72,6 +78,7 @@ async def test_vehicle_refresh_error(
|
||||
mock_vehicle_data: AsyncMock,
|
||||
side_effect: TeslaFleetError,
|
||||
state: ConfigEntryState,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Test coordinator refresh with an error."""
|
||||
mock_vehicle_data.side_effect = side_effect
|
||||
@ -107,6 +114,7 @@ async def test_energy_site_refresh_error(
|
||||
assert entry.state is state
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("entity_registry_enabled_by_default")
|
||||
async def test_vehicle_stream(
|
||||
hass: HomeAssistant,
|
||||
mock_add_listener: AsyncMock,
|
||||
@ -121,7 +129,7 @@ async def test_vehicle_stream(
|
||||
assert state.state == STATE_UNKNOWN
|
||||
|
||||
state = hass.states.get("binary_sensor.test_user_present")
|
||||
assert state.state == STATE_OFF
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
mock_add_listener.send(
|
||||
{
|
||||
|
@ -55,7 +55,6 @@ async def test_media_player_noscope(
|
||||
snapshot: SnapshotAssertion,
|
||||
entity_registry: er.EntityRegistry,
|
||||
mock_metadata: AsyncMock,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the media player entities are correct without required scope."""
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""Test the Teslemetry sensor platform."""
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
@ -26,6 +26,7 @@ async def test_sensors(
|
||||
entity_registry: er.EntityRegistry,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_vehicle_data: AsyncMock,
|
||||
mock_legacy: AsyncMock,
|
||||
) -> None:
|
||||
"""Tests that the sensor entities with the legacy polling are correct."""
|
||||
|
||||
@ -33,9 +34,7 @@ async def test_sensors(
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Force the vehicle to use polling
|
||||
with patch("tesla_fleet_api.teslemetry.Vehicle.pre2021", return_value=True):
|
||||
entry = await setup_platform(hass, [Platform.SENSOR])
|
||||
entry = await setup_platform(hass, [Platform.SENSOR])
|
||||
|
||||
assert_entities(hass, entry.entry_id, entity_registry, snapshot)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user