From ff9cbd14182b73b8560ab4a44d9d2d6296181dc5 Mon Sep 17 00:00:00 2001 From: Raman Gupta <7243222+raman325@users.noreply.github.com> Date: Tue, 15 Sep 2026 14:25:18 -0400 Subject: [PATCH] Use Vizio state extended capability detection (#181569) --- homeassistant/components/vizio/coordinator.py | 14 +++++++------ tests/components/vizio/test_init.py | 20 ++++++++++++++----- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/homeassistant/components/vizio/coordinator.py b/homeassistant/components/vizio/coordinator.py index 140f9aba9759..6a3b15e68335 100644 --- a/homeassistant/components/vizio/coordinator.py +++ b/homeassistant/components/vizio/coordinator.py @@ -18,6 +18,7 @@ from vizaio import ( VizioAuthError, VizioError, VizioNotFoundError, + VizioUnsupportedError, fetch_app_availability, fetch_remote_app_catalog, is_app_input, @@ -151,10 +152,9 @@ class VizioDeviceCoordinator(DataUpdateCoordinator[VizioDeviceData]): update_interval=SCAN_INTERVAL, ) self.device = device - # Modern TV firmware bundles power/input/app state into one endpoint; - # firmware without it never gains it, so probe only until the first - # URI_NOT_FOUND response. Audio devices do not support this endpoint. - self._use_state_extended = device.profile.has_inputs + # Supported firmware bundles power/input/app state into one endpoint. + # Probe until the library reports that the endpoint is unavailable. + self._use_state_extended = True @override async def _async_setup(self) -> None: @@ -193,8 +193,10 @@ class VizioDeviceCoordinator(DataUpdateCoordinator[VizioDeviceData]): try: state = await self.device.get_state_extended() except VizioAuthError as err: - raise ConfigEntryAuthFailed from err - except VizioNotFoundError: + if self.device.profile.requires_auth: + raise ConfigEntryAuthFailed from err + self._use_state_extended = False + except VizioNotFoundError, VizioUnsupportedError: self._use_state_extended = False except VizioError as err: raise self._update_failed() from err diff --git a/tests/components/vizio/test_init.py b/tests/components/vizio/test_init.py index 62fcc12a9abc..c66101c894b6 100644 --- a/tests/components/vizio/test_init.py +++ b/tests/components/vizio/test_init.py @@ -11,7 +11,9 @@ from vizaio import ( DeviceType, VizioAuthError, VizioConnectionError, + VizioError, VizioNotFoundError, + VizioUnsupportedError, ) from vizaio.profiles import SOUNDBAR_PROFILE @@ -228,18 +230,18 @@ async def test_state_extended_polling( @pytest.mark.usefixtures("vizio_connect") -async def test_soundbar_does_not_poll_state_extended( +async def test_soundbar_state_extended_auth_failure_falls_back( hass: HomeAssistant, mock_speaker_config_entry: MockConfigEntry, mock_vizio: AsyncMock, ) -> None: - """Test soundbars use the unauthenticated power endpoint.""" + """Test soundbars fall back when state_extended rejects no token.""" mock_vizio.profile = SOUNDBAR_PROFILE mock_vizio.get_state_extended.side_effect = VizioAuthError("token required") await setup_integration(hass, mock_speaker_config_entry) - mock_vizio.get_state_extended.assert_not_called() + mock_vizio.get_state_extended.assert_called_once() mock_vizio.get_power_state.assert_called_once() assert not hass.config_entries.flow.async_progress_by_handler(DOMAIN) @@ -259,15 +261,23 @@ async def test_state_extended_power_off( mock_vizio.get_settings.assert_not_called() +@pytest.mark.parametrize( + "error", + [ + pytest.param(VizioNotFoundError("not found"), id="not_found"), + pytest.param(VizioUnsupportedError("not supported"), id="unsupported"), + ], +) @pytest.mark.usefixtures("vizio_connect") async def test_state_extended_probed_only_once( hass: HomeAssistant, mock_tv_config_entry: MockConfigEntry, mock_vizio: AsyncMock, freezer: FrozenDateTimeFactory, + error: VizioError, ) -> None: - """Test firmware without state_extended is not re-probed every refresh.""" - mock_vizio.get_state_extended.side_effect = VizioNotFoundError("not supported") + """Test unavailable state_extended is not re-probed every refresh.""" + mock_vizio.get_state_extended.side_effect = error await setup_integration(hass, mock_tv_config_entry) mock_vizio.get_state_extended.reset_mock()