mirror of
https://github.com/home-assistant/core.git
synced 2026-09-26 01:11:51 -04:00
alexa_devices: remove stale values from known entities (#182401)
This commit is contained in:
@@ -127,6 +127,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -51,6 +51,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_routines_devices() -> None:
|
||||
current_routines = set(coordinator.api.routines)
|
||||
known_routines.intersection_update(current_routines)
|
||||
new_routines = current_routines - known_routines
|
||||
if new_routines:
|
||||
known_routines.update(new_routines)
|
||||
@@ -59,6 +60,7 @@ async def async_setup_entry(
|
||||
)
|
||||
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -47,6 +47,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -50,6 +50,7 @@ async def async_setup_entry(
|
||||
"""Add entities for newly discovered devices."""
|
||||
new_entities: list[AlexaDevicesMediaPlayer] = []
|
||||
|
||||
known_devices.intersection_update(coordinator.data)
|
||||
for serial_num, device in coordinator.data.items():
|
||||
if serial_num in known_devices or not device.media_player_supported:
|
||||
continue
|
||||
|
||||
@@ -63,6 +63,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -63,6 +63,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -159,6 +159,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -101,6 +101,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_device() -> None:
|
||||
current_devices = set(coordinator.data)
|
||||
known_devices.intersection_update(current_devices)
|
||||
new_devices = current_devices - known_devices
|
||||
if new_devices:
|
||||
known_devices.update(new_devices)
|
||||
|
||||
@@ -40,6 +40,7 @@ async def async_setup_entry(
|
||||
|
||||
def _check_lists() -> None:
|
||||
current_list_ids = {todo_list.id for todo_list in coordinator.api.todo_lists}
|
||||
known_list_ids.intersection_update(current_list_ids)
|
||||
new_list_ids = current_list_ids - known_list_ids
|
||||
if new_list_ids:
|
||||
known_list_ids.update(new_list_ids)
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
"""Tests for the Alexa Devices integration."""
|
||||
|
||||
from collections.abc import Mapping
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
from aioamazondevices.structures import AmazonDevice
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.alexa_devices.coordinator import SCAN_INTERVAL
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
|
||||
async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
||||
@@ -11,3 +18,33 @@ async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
|
||||
async def assert_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
config_entry: MockConfigEntry,
|
||||
entity_id: str,
|
||||
devices_with: Mapping[str, AmazonDevice],
|
||||
devices_without: Mapping[str, AmazonDevice],
|
||||
) -> None:
|
||||
"""Assert an entity is recreated when its device is removed and re-added."""
|
||||
mock_amazon_devices_client.get_devices_data.return_value = dict(devices_with)
|
||||
await setup_integration(hass, config_entry)
|
||||
|
||||
assert hass.states.get(entity_id) is not None
|
||||
|
||||
mock_amazon_devices_client.get_devices_data.return_value = dict(devices_without)
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
mock_amazon_devices_client.get_devices_data.return_value = dict(devices_with)
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id) is not None
|
||||
|
||||
@@ -17,7 +17,7 @@ from homeassistant.const import STATE_ON, STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr, entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN, TEST_DEVICE_2, TEST_DEVICE_2_SN
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
@@ -142,6 +142,24 @@ async def test_dynamic_device(
|
||||
assert state.state == STATE_ON
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entities are recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id="binary_sensor.echo_test_2_connectivity",
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1, TEST_DEVICE_2_SN: TEST_DEVICE_2},
|
||||
devices_without={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"key",
|
||||
[
|
||||
|
||||
@@ -13,8 +13,14 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util import slugify
|
||||
|
||||
from . import setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_USERNAME
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import (
|
||||
TEST_DEVICE_1,
|
||||
TEST_DEVICE_1_SN,
|
||||
TEST_DEVICE_2,
|
||||
TEST_DEVICE_2_SN,
|
||||
TEST_USERNAME,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
@@ -96,6 +102,55 @@ async def test_dynamic_entities(
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
|
||||
async def test_routine_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test routine button is recreated when removed and re-added."""
|
||||
|
||||
entity_id = f"button.{slugify(TEST_USERNAME)}_test_routine"
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get(entity_id) is not None
|
||||
|
||||
mock_amazon_devices_client.routines = []
|
||||
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
mock_amazon_devices_client.routines = ["Test Routine"]
|
||||
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id) is not None
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test device button is recreated when its device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id="button.echo_test_2_restart",
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1, TEST_DEVICE_2_SN: TEST_DEVICE_2},
|
||||
devices_without={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
)
|
||||
|
||||
|
||||
async def test_restart_button(
|
||||
hass: HomeAssistant,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from syrupy.assertion import SnapshotAssertion
|
||||
|
||||
@@ -9,8 +10,14 @@ from homeassistant.const import STATE_UNKNOWN, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from .const import TEST_DEVICE_1_SN, TEST_VOCAL_RECORD_EVENT
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import (
|
||||
TEST_DEVICE_1,
|
||||
TEST_DEVICE_1_SN,
|
||||
TEST_DEVICE_2,
|
||||
TEST_DEVICE_2_SN,
|
||||
TEST_VOCAL_RECORD_EVENT,
|
||||
)
|
||||
|
||||
from tests.common import MockConfigEntry, snapshot_platform
|
||||
|
||||
@@ -69,3 +76,21 @@ async def test_no_vocal_record_skips_event_trigger(
|
||||
assert (state := hass.states.get(ENTITY_ID))
|
||||
assert state.state == STATE_UNKNOWN
|
||||
assert state.attributes.get("event_type") is None
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entities are recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id="event.echo_test_2_voice_event",
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1, TEST_DEVICE_2_SN: TEST_DEVICE_2},
|
||||
devices_without={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
)
|
||||
|
||||
@@ -39,8 +39,8 @@ from homeassistant.const import ATTR_ENTITY_ID, STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from .const import TEST_DEVICE_1_SN
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
@@ -259,6 +259,24 @@ async def test_offline_device_recovers(
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entity is recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id=ENTITY_ID,
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
devices_without={},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("player_state", "expected_ha_state"),
|
||||
[
|
||||
|
||||
@@ -18,8 +18,8 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
from . import setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN, TEST_DEVICE_2, TEST_DEVICE_2_SN
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
@@ -104,6 +104,24 @@ async def test_offline_device(
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entities are recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id="notify.echo_test_2_announce",
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1, TEST_DEVICE_2_SN: TEST_DEVICE_2},
|
||||
devices_without={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
)
|
||||
|
||||
|
||||
async def test_announce_unavailable_when_communications_off(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
|
||||
@@ -21,7 +21,7 @@ from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN, TEST_DEVICE_2, TEST_DEVICE_2_SN
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
@@ -106,6 +106,24 @@ async def test_offline_device(
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entities are recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id=ENTITY_ID,
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
devices_without={},
|
||||
)
|
||||
|
||||
|
||||
async def test_service_select_option(
|
||||
hass: HomeAssistant,
|
||||
caplog: pytest.LogCaptureFixture,
|
||||
|
||||
@@ -18,8 +18,8 @@ from homeassistant.const import STATE_UNAVAILABLE, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from .const import TEST_DEVICE_1_SN
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN, TEST_DEVICE_2, TEST_DEVICE_2_SN
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
|
||||
@@ -103,6 +103,24 @@ async def test_offline_device(
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entities are recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id="sensor.echo_test_2_temperature",
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1, TEST_DEVICE_2_SN: TEST_DEVICE_2},
|
||||
devices_without={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("sensor", "api_value", "scale", "state_value", "unit"),
|
||||
[
|
||||
|
||||
@@ -24,7 +24,7 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from . import setup_integration
|
||||
from . import assert_device_removed_and_readded, setup_integration
|
||||
from .const import TEST_DEVICE_1, TEST_DEVICE_1_SN
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform
|
||||
@@ -164,6 +164,24 @@ async def test_offline_device(
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_device_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entities are recreated when a device is removed and re-added."""
|
||||
await assert_device_removed_and_readded(
|
||||
hass,
|
||||
freezer,
|
||||
mock_amazon_devices_client,
|
||||
mock_config_entry,
|
||||
entity_id=ENTITY_ID,
|
||||
devices_with={TEST_DEVICE_1_SN: TEST_DEVICE_1},
|
||||
devices_without={},
|
||||
)
|
||||
|
||||
|
||||
async def test_switch_communication(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
|
||||
@@ -570,6 +570,37 @@ async def test_dynamic_entities(
|
||||
assert hass.states.get(entity_id) is None
|
||||
|
||||
|
||||
async def test_list_removed_and_readded(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
mock_amazon_devices_client: AsyncMock,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
"""Test entity is recreated when a list is removed and re-added."""
|
||||
mock_amazon_devices_client.todo_lists = [MOCK_TODO_LIST]
|
||||
mock_amazon_devices_client.get_todo_list_items = AsyncMock(return_value={})
|
||||
|
||||
await setup_integration(hass, mock_config_entry)
|
||||
|
||||
assert hass.states.get(MOCK_TODO_LIST_ENTITY_ID) is not None
|
||||
|
||||
mock_amazon_devices_client.todo_lists = []
|
||||
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(MOCK_TODO_LIST_ENTITY_ID) is None
|
||||
|
||||
mock_amazon_devices_client.todo_lists = [MOCK_TODO_LIST]
|
||||
|
||||
freezer.tick(SCAN_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(MOCK_TODO_LIST_ENTITY_ID) is not None
|
||||
|
||||
|
||||
async def test_dynamic_add_list_and_add_item(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
|
||||
Reference in New Issue
Block a user