diff --git a/homeassistant/components/midea/__init__.py b/homeassistant/components/midea/__init__.py index 7fea47831bd4..044f65783787 100644 --- a/homeassistant/components/midea/__init__.py +++ b/homeassistant/components/midea/__init__.py @@ -128,6 +128,21 @@ async def async_setup_entry(hass: HomeAssistant, entry: MideaConfigEntry) -> boo translation_placeholders={"device_id": str(device_id)}, ) + unavailable_logged = False + + def _log_availability(_status: Mapping[str, Any]) -> None: + """Log once when the device goes offline and once when it is back.""" + nonlocal unavailable_logged + if not device.available and not unavailable_logged: + LOGGER.info("Device %s is unavailable", device_id) + unavailable_logged = True + elif device.available and unavailable_logged: + LOGGER.info("Device %s is back online", device_id) + unavailable_logged = False + + device.register_update(_log_availability) + entry.async_on_unload(partial(device.unregister_update, _log_availability)) + # The library's reconnect loop keeps retrying with a growing backoff # (up to 600s) without checking for a stop request while sleeping, so # device.close() alone cannot guarantee the background thread exits diff --git a/homeassistant/components/midea/quality_scale.yaml b/homeassistant/components/midea/quality_scale.yaml index 5a6763ccc94c..37d5c8dbbaf2 100644 --- a/homeassistant/components/midea/quality_scale.yaml +++ b/homeassistant/components/midea/quality_scale.yaml @@ -36,7 +36,7 @@ rules: docs-installation-parameters: done entity-unavailable: todo integration-owner: done - log-when-unavailable: todo + log-when-unavailable: done parallel-updates: done reauthentication-flow: todo test-coverage: done diff --git a/tests/components/midea/test_init.py b/tests/components/midea/test_init.py index b5186838ae4d..518d7ac6be90 100644 --- a/tests/components/midea/test_init.py +++ b/tests/components/midea/test_init.py @@ -1,8 +1,10 @@ """Tests for midea __init__.py.""" +import logging from unittest.mock import patch from midealocal.const import DeviceType, ProtocolVersion +import pytest from homeassistant.components.midea.const import CONF_SN, DOMAIN from homeassistant.config_entries import ConfigEntryState @@ -56,6 +58,36 @@ async def test_unload_entry(hass: HomeAssistant, config_entry: MockConfigEntry) assert ("close",) in device.calls +async def test_logs_once_when_device_unavailable_and_back( + hass: HomeAssistant, + config_entry: MockConfigEntry, + caplog: pytest.LogCaptureFixture, +) -> None: + """Test the device is logged once when it goes offline and once when it recovers.""" + config_entry.add_to_hass(hass) + device = DummyDevice(DeviceType.AC) + with patch( + "homeassistant.components.midea.device_selector", + return_value=device, + ): + await hass.config_entries.async_setup(config_entry.entry_id) + assert config_entry.state is ConfigEntryState.LOADED + + unavailable_msg = f"Device {TEST_DEVICE_ID} is unavailable" + back_online_msg = f"Device {TEST_DEVICE_ID} is back online" + + with caplog.at_level(logging.INFO, logger="homeassistant.components.midea"): + device.available = False + device.notify_update({"available": False}) + device.notify_update({"available": False}) + assert caplog.text.count(unavailable_msg) == 1 + + device.available = True + device.notify_update({"available": True}) + device.notify_update({"available": True}) + assert caplog.text.count(back_online_msg) == 1 + + async def test_async_setup_entry_paths( hass: HomeAssistant, config_entry: MockConfigEntry ) -> None: