mirror of
https://github.com/home-assistant/core.git
synced 2025-09-07 05:41:32 +02:00
Add stale device cleanup to Teslemetry (#144523)
This commit is contained in:
@@ -97,6 +97,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
# Create the stream
|
||||
stream: TeslemetryStream | None = None
|
||||
|
||||
# Remember each device identifier we create
|
||||
current_devices: set[tuple[str, str]] = set()
|
||||
|
||||
for product in products:
|
||||
if (
|
||||
"vin" in product
|
||||
@@ -116,6 +119,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
model=api.model,
|
||||
serial_number=vin,
|
||||
)
|
||||
current_devices.add((DOMAIN, vin))
|
||||
|
||||
# Create stream if required
|
||||
if not stream:
|
||||
@@ -171,6 +175,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
name=product.get("site_name", "Energy Site"),
|
||||
serial_number=str(site_id),
|
||||
)
|
||||
current_devices.add((DOMAIN, str(site_id)))
|
||||
|
||||
if wall_connector:
|
||||
for connector in product["components"]["wall_connectors"]:
|
||||
current_devices.add((DOMAIN, connector["din"]))
|
||||
|
||||
# Check live status endpoint works before creating its coordinator
|
||||
try:
|
||||
@@ -235,6 +244,19 @@ async def async_setup_entry(hass: HomeAssistant, entry: TeslemetryConfigEntry) -
|
||||
config_entry_id=entry.entry_id, **energysite.device
|
||||
)
|
||||
|
||||
# Remove devices that are no longer present
|
||||
for device_entry in dr.async_entries_for_config_entry(
|
||||
device_registry, entry.entry_id
|
||||
):
|
||||
if not any(
|
||||
identifier in current_devices for identifier in device_entry.identifiers
|
||||
):
|
||||
LOGGER.debug("Removing stale device %s", device_entry.id)
|
||||
device_registry.async_update_device(
|
||||
device_id=device_entry.id,
|
||||
remove_config_entry_id=entry.entry_id,
|
||||
)
|
||||
|
||||
# Setup Platforms
|
||||
entry.runtime_data = TeslemetryData(vehicles, energysites, scopes, stream)
|
||||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
"""Test the Teslemetry init."""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
@@ -11,6 +11,7 @@ from tesla_fleet_api.exceptions import (
|
||||
TeslaFleetError,
|
||||
)
|
||||
|
||||
from homeassistant.components.teslemetry.const import DOMAIN
|
||||
from homeassistant.components.teslemetry.coordinator import VEHICLE_INTERVAL
|
||||
from homeassistant.components.teslemetry.models import TeslemetryData
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
@@ -187,3 +188,94 @@ async def test_modern_no_poll(
|
||||
assert mock_vehicle_data.called is False
|
||||
freezer.tick(VEHICLE_INTERVAL)
|
||||
assert mock_vehicle_data.called is False
|
||||
|
||||
|
||||
async def test_stale_device_removal(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_products: AsyncMock,
|
||||
) -> None:
|
||||
"""Test removal of stale devices."""
|
||||
|
||||
# Setup the entry first to get a valid config_entry_id
|
||||
entry = await setup_platform(hass)
|
||||
|
||||
# Create a device that should be removed (with the valid entry_id)
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=entry.entry_id,
|
||||
identifiers={(DOMAIN, "stale-vin")},
|
||||
manufacturer="Tesla",
|
||||
name="Stale Vehicle",
|
||||
)
|
||||
|
||||
# Verify the stale device exists
|
||||
pre_devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
|
||||
stale_identifiers = {
|
||||
identifier for device in pre_devices for identifier in device.identifiers
|
||||
}
|
||||
assert (DOMAIN, "stale-vin") in stale_identifiers
|
||||
|
||||
# Update products with an empty response (no devices) and reload entry
|
||||
with patch(
|
||||
"tesla_fleet_api.teslemetry.Teslemetry.products",
|
||||
return_value={"response": []},
|
||||
):
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Get updated devices after reload
|
||||
post_devices = dr.async_entries_for_config_entry(
|
||||
device_registry, entry.entry_id
|
||||
)
|
||||
post_identifiers = {
|
||||
identifier for device in post_devices for identifier in device.identifiers
|
||||
}
|
||||
|
||||
# Verify the stale device has been removed
|
||||
assert (DOMAIN, "stale-vin") not in post_identifiers
|
||||
|
||||
# Verify the device itself has been completely removed from the registry
|
||||
# since it had no other config entries
|
||||
updated_device = device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, "stale-vin")}
|
||||
)
|
||||
assert updated_device is None
|
||||
|
||||
|
||||
async def test_device_retention_during_reload(
|
||||
hass: HomeAssistant,
|
||||
device_registry: dr.DeviceRegistry,
|
||||
mock_products: AsyncMock,
|
||||
) -> None:
|
||||
"""Test that valid devices are retained during a config entry reload."""
|
||||
# Setup entry with normal devices
|
||||
entry = await setup_platform(hass)
|
||||
|
||||
# Get initial device count and identifiers
|
||||
pre_devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
|
||||
pre_count = len(pre_devices)
|
||||
pre_identifiers = {
|
||||
identifier for device in pre_devices for identifier in device.identifiers
|
||||
}
|
||||
|
||||
# Make sure we have some devices
|
||||
assert pre_count > 0
|
||||
|
||||
# Save the original identifiers to compare after reload
|
||||
original_identifiers = pre_identifiers.copy()
|
||||
|
||||
# Reload the config entry with the same products data
|
||||
# The mock_products fixture will return the same data as during setup
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# Verify device count and identifiers after reload match pre-reload
|
||||
post_devices = dr.async_entries_for_config_entry(device_registry, entry.entry_id)
|
||||
post_count = len(post_devices)
|
||||
post_identifiers = {
|
||||
identifier for device in post_devices for identifier in device.identifiers
|
||||
}
|
||||
|
||||
# Since the products data didn't change, we should have the same devices
|
||||
assert post_count == pre_count
|
||||
assert post_identifiers == original_identifiers
|
||||
|
Reference in New Issue
Block a user