diff --git a/homeassistant/components/denonavr/media_player.py b/homeassistant/components/denonavr/media_player.py index ee02e7390ebc..cf697b993cec 100644 --- a/homeassistant/components/denonavr/media_player.py +++ b/homeassistant/components/denonavr/media_player.py @@ -20,6 +20,8 @@ from denonavr.const import ( from denonavr.exceptions import ( AvrCommandError, AvrForbiddenError, + AvrIncompleteResponseError, + AvrInvalidResponseError, AvrNetworkError, AvrProcessingError, AvrTimoutError, @@ -191,6 +193,17 @@ def async_log_errors[_DenonDeviceT: DenonDevice, **_P, _R]( self._receiver.host, ) self._attr_available = False + except AvrInvalidResponseError, AvrIncompleteResponseError: + available = False + if self.available: + _LOGGER.warning( + ( + "Denon AVR receiver at host %s returned malformed response. " + "Device is unavailable" + ), + self._receiver.host, + ) + self._attr_available = False except AvrCommandError as err: available = False _LOGGER.error( diff --git a/tests/components/denonavr/test_media_player.py b/tests/components/denonavr/test_media_player.py index 24b12c085074..662334a96968 100644 --- a/tests/components/denonavr/test_media_player.py +++ b/tests/components/denonavr/test_media_player.py @@ -1,7 +1,10 @@ """The tests for the denonavr media player platform.""" +from datetime import timedelta from unittest.mock import patch +from denonavr.exceptions import AvrIncompleteResponseError, AvrInvalidResponseError +from freezegun.api import FrozenDateTimeFactory import pytest from homeassistant.components import media_player @@ -18,10 +21,10 @@ from homeassistant.components.denonavr.services import ( SERVICE_SET_DYNAMIC_EQ, SERVICE_UPDATE_AUDYSSEY, ) -from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_MODEL +from homeassistant.const import ATTR_ENTITY_ID, CONF_HOST, CONF_MODEL, STATE_UNAVAILABLE from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry +from tests.common import MockConfigEntry, async_fire_time_changed TEST_HOST = "1.2.3.4" TEST_NAME = "Test_Receiver" @@ -137,3 +140,40 @@ async def test_update_audyssey(hass: HomeAssistant, client) -> None: await hass.async_block_till_done() client.async_update_audyssey.assert_called_once() + + +@pytest.mark.parametrize( + "exception", + [ + pytest.param( + AvrInvalidResponseError("XML parse error", "GET"), + id="invalid_response", + ), + pytest.param( + AvrIncompleteResponseError("Incomplete", "GET"), + id="incomplete_response", + ), + ], +) +async def test_malformed_response_marks_unavailable( + hass: HomeAssistant, + client, + freezer: FrozenDateTimeFactory, + exception: Exception, +) -> None: + """Test that malformed response errors mark the entity unavailable.""" + await setup_denonavr(hass) + + state = hass.states.get(ENTITY_ID) + assert state.state != STATE_UNAVAILABLE + + # Force polling by disabling telnet, then trigger the error + client.telnet_connected = False + client.telnet_healthy = False + client.async_update.side_effect = exception + freezer.tick(timedelta(seconds=11)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + state = hass.states.get(ENTITY_ID) + assert state.state == STATE_UNAVAILABLE