diff --git a/homeassistant/components/chacon_dio/manifest.json b/homeassistant/components/chacon_dio/manifest.json index edee24444f7..117982a7ab8 100644 --- a/homeassistant/components/chacon_dio/manifest.json +++ b/homeassistant/components/chacon_dio/manifest.json @@ -6,5 +6,5 @@ "documentation": "https://www.home-assistant.io/integrations/chacon_dio", "iot_class": "cloud_push", "loggers": ["dio_chacon_api"], - "requirements": ["dio-chacon-wifi-api==1.2.1"] + "requirements": ["dio-chacon-wifi-api==1.2.2"] } diff --git a/homeassistant/components/home_connect/coordinator.py b/homeassistant/components/home_connect/coordinator.py index 05a6a592ae8..0a0e0c78463 100644 --- a/homeassistant/components/home_connect/coordinator.py +++ b/homeassistant/components/home_connect/coordinator.py @@ -241,9 +241,7 @@ class HomeConnectCoordinator( appliance_data = await self._get_appliance_data( appliance_info, self.data.get(appliance_info.ha_id) ) - if event_message_ha_id in self.data: - self.data[event_message_ha_id].update(appliance_data) - else: + if event_message_ha_id not in self.data: self.data[event_message_ha_id] = appliance_data for listener, context in self._special_listeners.values(): if ( diff --git a/homeassistant/components/home_connect/select.py b/homeassistant/components/home_connect/select.py index c82e0686cb5..7d8b315b657 100644 --- a/homeassistant/components/home_connect/select.py +++ b/homeassistant/components/home_connect/select.py @@ -17,7 +17,6 @@ from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .common import setup_home_connect_entry from .const import ( - APPLIANCES_WITH_PROGRAMS, AVAILABLE_MAPS_ENUM, BEAN_AMOUNT_OPTIONS, BEAN_CONTAINER_OPTIONS, @@ -313,7 +312,7 @@ def _get_entities_for_appliance( HomeConnectProgramSelectEntity(entry.runtime_data, appliance, desc) for desc in PROGRAM_SELECT_ENTITY_DESCRIPTIONS ] - if appliance.info.type in APPLIANCES_WITH_PROGRAMS + if appliance.programs else [] ), *[ diff --git a/homeassistant/components/homekit_controller/manifest.json b/homeassistant/components/homekit_controller/manifest.json index 6562a3edcc9..dbcd2788c8a 100644 --- a/homeassistant/components/homekit_controller/manifest.json +++ b/homeassistant/components/homekit_controller/manifest.json @@ -14,6 +14,6 @@ "documentation": "https://www.home-assistant.io/integrations/homekit_controller", "iot_class": "local_push", "loggers": ["aiohomekit", "commentjson"], - "requirements": ["aiohomekit==3.2.13"], + "requirements": ["aiohomekit==3.2.14"], "zeroconf": ["_hap._tcp.local.", "_hap._udp.local."] } diff --git a/homeassistant/components/linkplay/media_player.py b/homeassistant/components/linkplay/media_player.py index 16b0d5f75f1..67aa424e3a2 100644 --- a/homeassistant/components/linkplay/media_player.py +++ b/homeassistant/components/linkplay/media_player.py @@ -2,6 +2,7 @@ from __future__ import annotations +from datetime import timedelta import logging from typing import Any @@ -120,6 +121,8 @@ SERVICE_PLAY_PRESET_SCHEMA = cv.make_entity_service_schema( ) RETRY_POLL_MAXIMUM = 3 +SCAN_INTERVAL = timedelta(seconds=5) +PARALLEL_UPDATES = 1 async def async_setup_entry( diff --git a/homeassistant/components/meteo_france/const.py b/homeassistant/components/meteo_france/const.py index 2230f43b754..382a56d50d7 100644 --- a/homeassistant/components/meteo_france/const.py +++ b/homeassistant/components/meteo_france/const.py @@ -40,7 +40,7 @@ ATTR_NEXT_RAIN_DT_REF = "forecast_time_ref" CONDITION_CLASSES: dict[str, list[str]] = { - ATTR_CONDITION_CLEAR_NIGHT: ["Nuit Claire", "Nuit claire"], + ATTR_CONDITION_CLEAR_NIGHT: ["Nuit Claire", "Nuit claire", "Ciel clair"], ATTR_CONDITION_CLOUDY: ["Très nuageux", "Couvert"], ATTR_CONDITION_FOG: [ "Brume ou bancs de brouillard", @@ -48,9 +48,10 @@ CONDITION_CLASSES: dict[str, list[str]] = { "Brouillard", "Brouillard givrant", "Bancs de Brouillard", + "Brouillard dense", ], ATTR_CONDITION_HAIL: ["Risque de grêle", "Risque de grèle"], - ATTR_CONDITION_LIGHTNING: ["Risque d'orages", "Orages"], + ATTR_CONDITION_LIGHTNING: ["Risque d'orages", "Orages", "Orage avec grêle"], ATTR_CONDITION_LIGHTNING_RAINY: [ "Pluie orageuses", "Pluies orageuses", @@ -62,6 +63,7 @@ CONDITION_CLASSES: dict[str, list[str]] = { "Éclaircies", "Eclaircies", "Peu nuageux", + "Variable", ], ATTR_CONDITION_POURING: ["Pluie forte"], ATTR_CONDITION_RAINY: [ @@ -74,6 +76,7 @@ CONDITION_CLASSES: dict[str, list[str]] = { "Pluie modérée", "Pluie / Averses", "Averses", + "Averses faibles", "Pluie", ], ATTR_CONDITION_SNOWY: [ @@ -81,6 +84,8 @@ CONDITION_CLASSES: dict[str, list[str]] = { "Neige", "Averses de neige", "Neige forte", + "Neige faible", + "Averses de neige faible", "Quelques flocons", ], ATTR_CONDITION_SNOWY_RAINY: ["Pluie et neige", "Pluie verglaçante"], diff --git a/homeassistant/components/random/strings.json b/homeassistant/components/random/strings.json index e5c5543e39f..bacd6dd5a17 100644 --- a/homeassistant/components/random/strings.json +++ b/homeassistant/components/random/strings.json @@ -84,8 +84,10 @@ "options": { "apparent_power": "[%key:component::sensor::entity_component::apparent_power::name%]", "aqi": "[%key:component::sensor::entity_component::aqi::name%]", + "area": "[%key:component::sensor::entity_component::area::name%]", "atmospheric_pressure": "[%key:component::sensor::entity_component::atmospheric_pressure::name%]", "battery": "[%key:component::sensor::entity_component::battery::name%]", + "blood_glucose_concentration": "[%key:component::sensor::entity_component::blood_glucose_concentration::name%]", "carbon_dioxide": "[%key:component::sensor::entity_component::carbon_dioxide::name%]", "carbon_monoxide": "[%key:component::sensor::entity_component::carbon_monoxide::name%]", "conductivity": "[%key:component::sensor::entity_component::conductivity::name%]", diff --git a/homeassistant/components/smartthings/manifest.json b/homeassistant/components/smartthings/manifest.json index 4cd27e49664..c682b5402c4 100644 --- a/homeassistant/components/smartthings/manifest.json +++ b/homeassistant/components/smartthings/manifest.json @@ -30,5 +30,5 @@ "iot_class": "cloud_push", "loggers": ["pysmartthings"], "quality_scale": "bronze", - "requirements": ["pysmartthings==3.0.4"] + "requirements": ["pysmartthings==3.0.5"] } diff --git a/homeassistant/components/vodafone_station/__init__.py b/homeassistant/components/vodafone_station/__init__.py index 9f118fe4fbd..b4ba5663ac2 100644 --- a/homeassistant/components/vodafone_station/__init__.py +++ b/homeassistant/components/vodafone_station/__init__.py @@ -3,7 +3,6 @@ from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME, Platform from homeassistant.core import HomeAssistant -from .const import DOMAIN from .coordinator import VodafoneConfigEntry, VodafoneStationRouter PLATFORMS = [Platform.BUTTON, Platform.DEVICE_TRACKER, Platform.SENSOR] @@ -36,7 +35,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: VodafoneConfigEntry) -> coordinator = entry.runtime_data await coordinator.api.logout() await coordinator.api.close() - hass.data[DOMAIN].pop(entry.entry_id) return unload_ok diff --git a/homeassistant/const.py b/homeassistant/const.py index 6c29fbb42cd..25c5eaf019b 100644 --- a/homeassistant/const.py +++ b/homeassistant/const.py @@ -25,7 +25,7 @@ if TYPE_CHECKING: APPLICATION_NAME: Final = "HomeAssistant" MAJOR_VERSION: Final = 2025 MINOR_VERSION: Final = 4 -PATCH_VERSION: Final = "3" +PATCH_VERSION: Final = "4" __short_version__: Final = f"{MAJOR_VERSION}.{MINOR_VERSION}" __version__: Final = f"{__short_version__}.{PATCH_VERSION}" REQUIRED_PYTHON_VER: Final[tuple[int, int, int]] = (3, 13, 0) diff --git a/pyproject.toml b/pyproject.toml index d365ec9c8c5..c1a6ffbadff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,10 @@ [build-system] -requires = ["setuptools==77.0.3"] +requires = ["setuptools==78.1.1"] build-backend = "setuptools.build_meta" [project] name = "homeassistant" -version = "2025.4.3" +version = "2025.4.4" license = "Apache-2.0" license-files = ["LICENSE*", "homeassistant/backports/LICENSE*"] description = "Open-source home automation platform running on Python 3." diff --git a/requirements_all.txt b/requirements_all.txt index 81284e09591..9c8bf7ae9aa 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -267,7 +267,7 @@ aiohasupervisor==0.3.0 aiohomeconnect==0.16.3 # homeassistant.components.homekit_controller -aiohomekit==3.2.13 +aiohomekit==3.2.14 # homeassistant.components.mcp_server aiohttp_sse==2.2.0 @@ -784,7 +784,7 @@ devolo-home-control-api==0.18.3 devolo-plc-api==1.5.1 # homeassistant.components.chacon_dio -dio-chacon-wifi-api==1.2.1 +dio-chacon-wifi-api==1.2.2 # homeassistant.components.directv directv==0.4.0 @@ -2319,7 +2319,7 @@ pysma==0.7.5 pysmappee==0.2.29 # homeassistant.components.smartthings -pysmartthings==3.0.4 +pysmartthings==3.0.5 # homeassistant.components.smarty pysmarty2==0.10.2 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index c2fc32e6503..2abcb85e499 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -252,7 +252,7 @@ aiohasupervisor==0.3.0 aiohomeconnect==0.16.3 # homeassistant.components.homekit_controller -aiohomekit==3.2.13 +aiohomekit==3.2.14 # homeassistant.components.mcp_server aiohttp_sse==2.2.0 @@ -675,7 +675,7 @@ devolo-home-control-api==0.18.3 devolo-plc-api==1.5.1 # homeassistant.components.chacon_dio -dio-chacon-wifi-api==1.2.1 +dio-chacon-wifi-api==1.2.2 # homeassistant.components.directv directv==0.4.0 @@ -1889,7 +1889,7 @@ pysma==0.7.5 pysmappee==0.2.29 # homeassistant.components.smartthings -pysmartthings==3.0.4 +pysmartthings==3.0.5 # homeassistant.components.smarty pysmarty2==0.10.2 diff --git a/script/licenses.py b/script/licenses.py index ab8ab62eb1d..aed3bec9998 100644 --- a/script/licenses.py +++ b/script/licenses.py @@ -191,7 +191,6 @@ EXCEPTIONS = { "enocean", # https://github.com/kipe/enocean/pull/142 "imutils", # https://github.com/PyImageSearch/imutils/pull/292 "iso4217", # Public domain - "jaraco.itertools", # MIT - https://github.com/jaraco/jaraco.itertools/issues/21 "kiwiki_client", # https://github.com/c7h/kiwiki_client/pull/6 "ld2410-ble", # https://github.com/930913/ld2410-ble/pull/7 "maxcube-api", # https://github.com/uebelack/python-maxcube-api/pull/48 @@ -205,6 +204,11 @@ EXCEPTIONS = { "repoze.lru", "sharp_aquos_rc", # https://github.com/jmoore987/sharp_aquos_rc/pull/14 "tapsaff", # https://github.com/bazwilliams/python-taps-aff/pull/5 + # --- + # https://github.com/jaraco/skeleton/pull/170 + # https://github.com/jaraco/skeleton/pull/171 + "jaraco.itertools", # MIT - https://github.com/jaraco/jaraco.itertools/issues/21 + "setuptools", # MIT } TODO = { diff --git a/tests/components/home_connect/fixtures/programs.json b/tests/components/home_connect/fixtures/programs.json index bba1a5d2721..e8d8bd24705 100644 --- a/tests/components/home_connect/fixtures/programs.json +++ b/tests/components/home_connect/fixtures/programs.json @@ -181,5 +181,29 @@ } ] } + }, + "Hood": { + "data": { + "programs": [ + { + "key": "Cooking.Common.Program.Hood.Automatic", + "constraints": { + "execution": "selectandstart" + } + }, + { + "key": "Cooking.Common.Program.Hood.Venting", + "constraints": { + "execution": "selectandstart" + } + }, + { + "key": "Cooking.Common.Program.Hood.DelayedShutOff", + "constraints": { + "execution": "selectandstart" + } + } + ] + } } } diff --git a/tests/components/home_connect/snapshots/test_diagnostics.ambr b/tests/components/home_connect/snapshots/test_diagnostics.ambr index 28f45ce97ba..535119b941c 100644 --- a/tests/components/home_connect/snapshots/test_diagnostics.ambr +++ b/tests/components/home_connect/snapshots/test_diagnostics.ambr @@ -90,6 +90,9 @@ 'ha_id': 'BOSCH-HCS000000-D00000000004', 'name': 'Hood', 'programs': list([ + 'Cooking.Common.Program.Hood.Automatic', + 'Cooking.Common.Program.Hood.Venting', + 'Cooking.Common.Program.Hood.DelayedShutOff', ]), 'settings': dict({ 'BSH.Common.Setting.AmbientLightBrightness': 70, diff --git a/tests/components/home_connect/test_binary_sensor.py b/tests/components/home_connect/test_binary_sensor.py index ce879a38de5..672d04e108b 100644 --- a/tests/components/home_connect/test_binary_sensor.py +++ b/tests/components/home_connect/test_binary_sensor.py @@ -11,6 +11,7 @@ from aiohomeconnect.model import ( EventMessage, EventType, HomeAppliance, + StatusKey, ) from aiohomeconnect.model.error import HomeConnectApiError import pytest @@ -115,9 +116,19 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["Washer"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Washer", + (StatusKey.BSH_COMMON_REMOTE_CONTROL_ACTIVE,), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -148,7 +159,17 @@ async def test_connected_devices( device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + assert entity_registry.async_get_entity_id( + Platform.BINARY_SENSOR, + DOMAIN, + f"{appliance.ha_id}-{EventKey.BSH_COMMON_APPLIANCE_CONNECTED}", + ) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.BINARY_SENSOR, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -161,10 +182,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in (*keys_to_check, EventKey.BSH_COMMON_APPLIANCE_CONNECTED): + assert entity_registry.async_get_entity_id( + Platform.BINARY_SENSOR, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.usefixtures("entity_registry_enabled_by_default") diff --git a/tests/components/home_connect/test_button.py b/tests/components/home_connect/test_button.py index f894494792d..c96fe840238 100644 --- a/tests/components/home_connect/test_button.py +++ b/tests/components/home_connect/test_button.py @@ -99,9 +99,19 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["Washer"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Washer", + (CommandKey.BSH_COMMON_PAUSE_PROGRAM,), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -116,7 +126,7 @@ async def test_connected_devices( not be obtained while disconnected and once connected, the entities are added. """ get_available_commands_original_mock = client.get_available_commands - get_available_programs_mock = client.get_available_programs + get_all_programs_mock = client.get_all_programs async def get_available_commands_side_effect(ha_id: str): if ha_id == appliance.ha_id: @@ -125,28 +135,36 @@ async def test_connected_devices( ) return await get_available_commands_original_mock.side_effect(ha_id) - async def get_available_programs_side_effect(ha_id: str): + async def get_all_programs_side_effect(ha_id: str): if ha_id == appliance.ha_id: raise HomeConnectApiError( "SDK.Error.HomeAppliance.Connection.Initialization.Failed" ) - return await get_available_programs_mock.side_effect(ha_id) + return await get_all_programs_mock.side_effect(ha_id) client.get_available_commands = AsyncMock( side_effect=get_available_commands_side_effect ) - client.get_available_programs = AsyncMock( - side_effect=get_available_programs_side_effect - ) + client.get_all_programs = AsyncMock(side_effect=get_all_programs_side_effect) assert config_entry.state == ConfigEntryState.NOT_LOADED assert await integration_setup(client) assert config_entry.state == ConfigEntryState.LOADED client.get_available_commands = get_available_commands_original_mock - client.get_available_programs = get_available_programs_mock + client.get_all_programs = get_all_programs_mock device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + assert entity_registry.async_get_entity_id( + Platform.BUTTON, + DOMAIN, + f"{appliance.ha_id}-StopProgram", + ) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.BUTTON, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -159,10 +177,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in (*keys_to_check, "StopProgram"): + assert entity_registry.async_get_entity_id( + Platform.BUTTON, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.parametrize("appliance", ["Washer"], indirect=True) diff --git a/tests/components/home_connect/test_light.py b/tests/components/home_connect/test_light.py index 50a1a1e374a..298eead1737 100644 --- a/tests/components/home_connect/test_light.py +++ b/tests/components/home_connect/test_light.py @@ -119,9 +119,19 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["Hood"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Hood", + (SettingKey.COOKING_COMMON_LIGHTING,), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -136,7 +146,6 @@ async def test_connected_devices( not be obtained while disconnected and once connected, the entities are added. """ get_settings_original_mock = client.get_settings - get_available_programs_mock = client.get_available_programs async def get_settings_side_effect(ha_id: str): if ha_id == appliance.ha_id: @@ -145,26 +154,20 @@ async def test_connected_devices( ) return await get_settings_original_mock.side_effect(ha_id) - async def get_available_programs_side_effect(ha_id: str): - if ha_id == appliance.ha_id: - raise HomeConnectApiError( - "SDK.Error.HomeAppliance.Connection.Initialization.Failed" - ) - return await get_available_programs_mock.side_effect(ha_id) - client.get_settings = AsyncMock(side_effect=get_settings_side_effect) - client.get_available_programs = AsyncMock( - side_effect=get_available_programs_side_effect - ) assert config_entry.state == ConfigEntryState.NOT_LOADED assert await integration_setup(client) assert config_entry.state == ConfigEntryState.LOADED client.get_settings = get_settings_original_mock - client.get_available_programs = get_available_programs_mock device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.LIGHT, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -177,10 +180,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in keys_to_check: + assert entity_registry.async_get_entity_id( + Platform.LIGHT, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.parametrize("appliance", ["Hood"], indirect=True) diff --git a/tests/components/home_connect/test_number.py b/tests/components/home_connect/test_number.py index 1de384303ce..7e89f66683b 100644 --- a/tests/components/home_connect/test_number.py +++ b/tests/components/home_connect/test_number.py @@ -135,9 +135,21 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["FridgeFreezer"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "FridgeFreezer", + ( + SettingKey.REFRIGERATION_FRIDGE_FREEZER_SETPOINT_TEMPERATURE_REFRIGERATOR, + ), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -168,7 +180,12 @@ async def test_connected_devices( device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.NUMBER, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -181,10 +198,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in keys_to_check: + assert entity_registry.async_get_entity_id( + Platform.NUMBER, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.parametrize("appliance", ["FridgeFreezer"], indirect=True) diff --git a/tests/components/home_connect/test_select.py b/tests/components/home_connect/test_select.py index f6009640f72..4f3f804eb06 100644 --- a/tests/components/home_connect/test_select.py +++ b/tests/components/home_connect/test_select.py @@ -20,6 +20,7 @@ from aiohomeconnect.model import ( ) from aiohomeconnect.model.error import ( ActiveProgramNotSetError, + HomeConnectApiError, HomeConnectError, SelectedProgramNotSetError, TooManyRequestsError, @@ -138,9 +139,23 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["Washer"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Hood", + ( + EventKey.BSH_COMMON_ROOT_ACTIVE_PROGRAM, + EventKey.BSH_COMMON_ROOT_SELECTED_PROGRAM, + SettingKey.COOKING_HOOD_COLOR_TEMPERATURE, + ), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -154,13 +169,39 @@ async def test_connected_devices( Specifically those devices whose settings, status, etc. could not be obtained while disconnected and once connected, the entities are added. """ + get_settings_original_mock = client.get_settings + get_all_programs_mock = client.get_all_programs + async def get_settings_side_effect(ha_id: str): + if ha_id == appliance.ha_id: + raise HomeConnectApiError( + "SDK.Error.HomeAppliance.Connection.Initialization.Failed" + ) + return await get_settings_original_mock.side_effect(ha_id) + + async def get_all_programs_side_effect(ha_id: str): + if ha_id == appliance.ha_id: + raise HomeConnectApiError( + "SDK.Error.HomeAppliance.Connection.Initialization.Failed" + ) + return await get_all_programs_mock.side_effect(ha_id) + + client.get_settings = AsyncMock(side_effect=get_settings_side_effect) + client.get_all_programs = AsyncMock(side_effect=get_all_programs_side_effect) assert config_entry.state == ConfigEntryState.NOT_LOADED assert await integration_setup(client) assert config_entry.state == ConfigEntryState.LOADED + client.get_settings = get_settings_original_mock + client.get_all_programs = get_all_programs_mock device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.SELECT, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -173,10 +214,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert entity_entries + for key in keys_to_check: + assert entity_registry.async_get_entity_id( + Platform.SELECT, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.parametrize("appliance", ["Washer"], indirect=True) diff --git a/tests/components/home_connect/test_sensor.py b/tests/components/home_connect/test_sensor.py index f30723af7fa..f0481318a37 100644 --- a/tests/components/home_connect/test_sensor.py +++ b/tests/components/home_connect/test_sensor.py @@ -154,9 +154,19 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["Washer"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Washer", + (StatusKey.BSH_COMMON_OPERATION_STATE,), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -187,7 +197,12 @@ async def test_connected_devices( device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.SENSOR, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -200,10 +215,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in keys_to_check: + assert entity_registry.async_get_entity_id( + Platform.SENSOR, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.parametrize("appliance", [TEST_HC_APP], indirect=True) diff --git a/tests/components/home_connect/test_switch.py b/tests/components/home_connect/test_switch.py index 01f9cad5d2e..2f8b95ceab2 100644 --- a/tests/components/home_connect/test_switch.py +++ b/tests/components/home_connect/test_switch.py @@ -147,9 +147,23 @@ async def test_paired_depaired_devices_flow( assert entity_registry.async_get(entity_entry.entity_id) -@pytest.mark.parametrize("appliance", ["Washer"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Washer", + ( + SettingKey.BSH_COMMON_POWER_STATE, + SettingKey.BSH_COMMON_CHILD_LOCK, + "Program Cotton", + ), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -164,7 +178,7 @@ async def test_connected_devices( not be obtained while disconnected and once connected, the entities are added. """ get_settings_original_mock = client.get_settings - get_available_programs_mock = client.get_available_programs + get_all_programs_mock = client.get_all_programs async def get_settings_side_effect(ha_id: str): if ha_id == appliance.ha_id: @@ -173,26 +187,29 @@ async def test_connected_devices( ) return await get_settings_original_mock.side_effect(ha_id) - async def get_available_programs_side_effect(ha_id: str): + async def get_all_programs_side_effect(ha_id: str): if ha_id == appliance.ha_id: raise HomeConnectApiError( "SDK.Error.HomeAppliance.Connection.Initialization.Failed" ) - return await get_available_programs_mock.side_effect(ha_id) + return await get_all_programs_mock.side_effect(ha_id) client.get_settings = AsyncMock(side_effect=get_settings_side_effect) - client.get_available_programs = AsyncMock( - side_effect=get_available_programs_side_effect - ) + client.get_all_programs = AsyncMock(side_effect=get_all_programs_side_effect) assert config_entry.state == ConfigEntryState.NOT_LOADED assert await integration_setup(client) assert config_entry.state == ConfigEntryState.LOADED client.get_settings = get_settings_original_mock - client.get_available_programs = get_available_programs_mock + client.get_all_programs = get_all_programs_mock device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.SWITCH, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -205,10 +222,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in keys_to_check: + assert entity_registry.async_get_entity_id( + Platform.SWITCH, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.usefixtures("entity_registry_enabled_by_default") diff --git a/tests/components/home_connect/test_time.py b/tests/components/home_connect/test_time.py index 8c23a09053a..34781c29eb8 100644 --- a/tests/components/home_connect/test_time.py +++ b/tests/components/home_connect/test_time.py @@ -113,9 +113,19 @@ async def test_paired_depaired_devices_flow( @pytest.mark.usefixtures("entity_registry_enabled_by_default") -@pytest.mark.parametrize("appliance", ["Oven"], indirect=True) +@pytest.mark.parametrize( + ("appliance", "keys_to_check"), + [ + ( + "Oven", + (SettingKey.BSH_COMMON_ALARM_CLOCK,), + ) + ], + indirect=["appliance"], +) async def test_connected_devices( appliance: HomeAppliance, + keys_to_check: tuple, hass: HomeAssistant, config_entry: MockConfigEntry, integration_setup: Callable[[MagicMock], Awaitable[bool]], @@ -146,7 +156,12 @@ async def test_connected_devices( device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) assert device - entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) + for key in keys_to_check: + assert not entity_registry.async_get_entity_id( + Platform.TIME, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) await client.add_events( [ @@ -159,10 +174,12 @@ async def test_connected_devices( ) await hass.async_block_till_done() - device = device_registry.async_get_device(identifiers={(DOMAIN, appliance.ha_id)}) - assert device - new_entity_entries = entity_registry.entities.get_entries_for_device_id(device.id) - assert len(new_entity_entries) > len(entity_entries) + for key in keys_to_check: + assert entity_registry.async_get_entity_id( + Platform.TIME, + DOMAIN, + f"{appliance.ha_id}-{key}", + ) @pytest.mark.usefixtures("entity_registry_enabled_by_default") diff --git a/tests/components/vodafone_station/conftest.py b/tests/components/vodafone_station/conftest.py index a065a1e8065..778d8fdaa41 100644 --- a/tests/components/vodafone_station/conftest.py +++ b/tests/components/vodafone_station/conftest.py @@ -5,7 +5,7 @@ from datetime import UTC, datetime from aiovodafone import VodafoneStationDevice import pytest -from homeassistant.components.vodafone_station import DOMAIN +from homeassistant.components.vodafone_station.const import DOMAIN from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME from .const import DEVICE_1_HOST, DEVICE_1_MAC, DEVICE_2_MAC diff --git a/tests/components/vodafone_station/test_init.py b/tests/components/vodafone_station/test_init.py index 12b3c3dce8f..053f0a95fe4 100644 --- a/tests/components/vodafone_station/test_init.py +++ b/tests/components/vodafone_station/test_init.py @@ -3,6 +3,8 @@ from unittest.mock import AsyncMock from homeassistant.components.device_tracker import CONF_CONSIDER_HOME +from homeassistant.components.vodafone_station.const import DOMAIN +from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType @@ -31,3 +33,21 @@ async def test_reload_config_entry_with_options( assert result["data"] == { CONF_CONSIDER_HOME: 37, } + + +async def test_unload_entry( + hass: HomeAssistant, + mock_vodafone_station_router: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test unloading the config entry.""" + await setup_integration(hass, mock_config_entry) + + assert len(hass.config_entries.async_entries(DOMAIN)) == 1 + assert mock_config_entry.state is ConfigEntryState.LOADED + + assert await hass.config_entries.async_unload(mock_config_entry.entry_id) + await hass.async_block_till_done() + + assert mock_config_entry.state is ConfigEntryState.NOT_LOADED + assert not hass.data.get(DOMAIN)