From 4c01a1969d5b00e5b3fc56e512d652a613cda6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ab=C3=ADlio=20Costa?= Date: Tue, 8 Sep 2026 16:57:41 +0100 Subject: [PATCH] Fix whirlpool initial availability (#181655) --- homeassistant/components/whirlpool/entity.py | 1 + tests/components/whirlpool/test_climate.py | 47 ------------- tests/components/whirlpool/test_init.py | 69 +++++++++++++++++++- 3 files changed, 68 insertions(+), 49 deletions(-) diff --git a/homeassistant/components/whirlpool/entity.py b/homeassistant/components/whirlpool/entity.py index 404243a9d756..1578a2ba8816 100644 --- a/homeassistant/components/whirlpool/entity.py +++ b/homeassistant/components/whirlpool/entity.py @@ -39,6 +39,7 @@ class WhirlpoolEntity(Entity): async def async_added_to_hass(self) -> None: """Register attribute updates callback.""" self._appliance.register_attr_callback(self._async_attr_callback) + self._async_attr_callback() @override async def async_will_remove_from_hass(self) -> None: diff --git a/tests/components/whirlpool/test_climate.py b/tests/components/whirlpool/test_climate.py index 33fca5aeb08b..e5b7abf098ab 100644 --- a/tests/components/whirlpool/test_climate.py +++ b/tests/components/whirlpool/test_climate.py @@ -362,50 +362,3 @@ async def test_service_unsupported( {ATTR_ENTITY_ID: entity_id, **service_data}, blocking=True, ) - - -async def test_availability_logs( - hass: HomeAssistant, - caplog: pytest.LogCaptureFixture, - multiple_climate_entities: tuple[str, str], - request: pytest.FixtureRequest, -) -> None: - """Test that availability status changes are logged correctly.""" - entity_id, mock_fixture = multiple_climate_entities - mock_instance = request.getfixturevalue(mock_fixture) - await init_integration(hass) - - caplog.clear() - mock_instance.get_online.return_value = True - state = await update_ac_state(hass, entity_id, mock_instance) - assert state.state != STATE_UNAVAILABLE - - # Make the entity go offline - should log unavailable message - mock_instance.get_online.return_value = False - state = await update_ac_state(hass, entity_id, mock_instance) - assert state.state == STATE_UNAVAILABLE - unavailable_log = f"The entity {entity_id} is unavailable" - assert unavailable_log in caplog.text - - # Clear logs and update the offline entity again - should NOT log again - caplog.clear() - state = await update_ac_state(hass, entity_id, mock_instance) - assert unavailable_log not in caplog.text - - # Now bring the entity back online - should log back online message - mock_instance.get_online.return_value = True - state = await update_ac_state(hass, entity_id, mock_instance) - assert state.state != STATE_UNAVAILABLE - available_log = f"The entity {entity_id} is back online" - assert available_log in caplog.text - - # Clear logs and make update again - should NOT log again - caplog.clear() - state = await update_ac_state(hass, entity_id, mock_instance) - assert available_log not in caplog.text - - # Test offline again to ensure the flag resets properly - mock_instance.get_online.return_value = False - state = await update_ac_state(hass, entity_id, mock_instance) - assert state.state == STATE_UNAVAILABLE - assert unavailable_log in caplog.text diff --git a/tests/components/whirlpool/test_init.py b/tests/components/whirlpool/test_init.py index a5c25b7ea913..90ba8111bb81 100644 --- a/tests/components/whirlpool/test_init.py +++ b/tests/components/whirlpool/test_init.py @@ -1,5 +1,6 @@ """Test the Whirlpool Sixth Sense init.""" +import logging from unittest.mock import AsyncMock, MagicMock import aiohttp @@ -9,10 +10,16 @@ from whirlpool.backendselector import Brand, Region from homeassistant.components.whirlpool.const import DOMAIN from homeassistant.config_entries import ConfigEntryState -from homeassistant.const import CONF_PASSWORD, CONF_REGION, CONF_USERNAME +from homeassistant.const import ( + CONF_PASSWORD, + CONF_REGION, + CONF_USERNAME, + STATE_UNAVAILABLE, +) from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er -from . import init_integration, init_integration_with_entry +from . import init_integration, init_integration_with_entry, trigger_attr_callback from tests.common import MockConfigEntry @@ -145,3 +152,61 @@ async def test_unload_entry(hass: HomeAssistant) -> None: assert entry.state is ConfigEntryState.NOT_LOADED assert not hass.data.get(DOMAIN) + + +@pytest.mark.usefixtures("entity_registry_enabled_by_default") +async def test_availability_logs( + hass: HomeAssistant, + caplog: pytest.LogCaptureFixture, + entity_registry: er.EntityRegistry, + mock_appliances_manager_api: MagicMock, +) -> None: + """Test availability and transition logs for every appliance entity.""" + manager = mock_appliances_manager_api.return_value + appliances = [ + *manager.aircons, + *manager.washers, + *manager.dryers, + *manager.ovens, + *manager.refrigerators, + ] + for appliance in appliances: + appliance.get_online.return_value = False + entry = await init_integration(hass) + + entity_ids = { + entity.entity_id + for entity in er.async_entries_for_config_entry(entity_registry, entry.entry_id) + } + assert entity_ids + for entity_id in entity_ids: + assert (state := hass.states.get(entity_id)) is not None + assert state.state == STATE_UNAVAILABLE + + # Repeated updates must not log again; subsequent transitions must log again. + for online, message, log_count in ( + (True, "is back online", 1), + (False, "is unavailable", 1), + (False, "is unavailable", 0), + (True, "is back online", 1), + (True, "is back online", 0), + (False, "is unavailable", 1), + ): + caplog.clear() + for appliance in appliances: + appliance.get_online.return_value = online + await trigger_attr_callback(hass, appliance) + + for entity_id in entity_ids: + assert (state := hass.states.get(entity_id)) is not None + assert (state.state != STATE_UNAVAILABLE) is online + assert ( + caplog.record_tuples.count( + ( + "homeassistant.components.whirlpool.entity", + logging.INFO, + f"The entity {entity_id} {message}", + ) + ) + == log_count + )