1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-20 10:59:24 +00:00

Fix LG webOS TV entity availability status (#155164)

This commit is contained in:
Shay Levy
2025-10-26 07:20:24 +02:00
committed by GitHub
parent 6fa73f7f6a
commit 07b6358fff
3 changed files with 115 additions and 18 deletions

View File

@@ -162,6 +162,7 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity):
self._entry = entry
self._client = entry.runtime_data
self._attr_assumed_state = True
self._unavailable_logged = False
self._device_name = entry.title
self._attr_unique_id = entry.unique_id
self._sources = entry.options.get(CONF_SOURCES)
@@ -348,19 +349,31 @@ class LgWebOSMediaPlayerEntity(RestoreEntity, MediaPlayerEntity):
):
self._source_list["Live TV"] = app
def _set_availability(self, available: bool) -> None:
"""Set availability and log changes only once."""
self._attr_available = available
if not available and not self._unavailable_logged:
_LOGGER.info("LG webOS TV entity %s is unavailable", self.entity_id)
self._unavailable_logged = True
elif available and self._unavailable_logged:
_LOGGER.info("LG webOS TV entity %s is back online", self.entity_id)
self._unavailable_logged = False
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
async def async_update(self) -> None:
"""Connect."""
if self._client.is_connected():
return
with suppress(*WEBOSTV_EXCEPTIONS):
try:
await self._client.connect()
except WebOsTvPairError:
self._entry.async_start_reauth(self.hass)
else:
update_client_key(self.hass, self._entry)
try:
await self._client.connect()
except WEBOSTV_EXCEPTIONS:
self._set_availability(bool(self._turn_on))
except WebOsTvPairError:
self._entry.async_start_reauth(self.hass)
else:
self._set_availability(True)
update_client_key(self.hass, self._entry)
@property
def supported_features(self) -> MediaPlayerEntityFeature:

View File

@@ -26,9 +26,9 @@ rules:
config-entry-unloading: done
docs-configuration-parameters: done
docs-installation-parameters: done
entity-unavailable: todo
entity-unavailable: done
integration-owner: done
log-when-unavailable: todo
log-when-unavailable: done
parallel-updates: done
reauthentication-flow: done
test-coverage: done

View File

@@ -59,6 +59,7 @@ from homeassistant.const import (
SERVICE_VOLUME_SET,
SERVICE_VOLUME_UP,
STATE_OFF,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant, State
from homeassistant.exceptions import HomeAssistantError
@@ -73,6 +74,16 @@ from tests.test_util.aiohttp import AiohttpClientMocker
from tests.typing import ClientSessionGenerator
async def mock_scan_interval(
hass: HomeAssistant,
freezer: FrozenDateTimeFactory,
) -> None:
"""Mock update interval to force an update."""
freezer.tick(timedelta(seconds=11))
async_fire_time_changed(hass)
await hass.async_block_till_done()
@pytest.mark.parametrize(
("service", "attr_data", "client_call"),
[
@@ -488,9 +499,7 @@ async def test_client_disconnected(
client.is_connected.return_value = False
client.connect.side_effect = TimeoutError
freezer.tick(timedelta(seconds=20))
async_fire_time_changed(hass)
await hass.async_block_till_done()
await mock_scan_interval(hass, freezer)
assert "TimeoutError" not in caplog.text
@@ -506,9 +515,7 @@ async def test_client_key_update_on_connect(
client.is_connected.return_value = False
client.client_key = "new_key"
freezer.tick(timedelta(seconds=20))
async_fire_time_changed(hass)
await hass.async_block_till_done()
await mock_scan_interval(hass, freezer)
assert config_entry.data[CONF_CLIENT_SECRET] == client.client_key
@@ -849,9 +856,7 @@ async def test_reauth_reconnect(
assert entry.state is ConfigEntryState.LOADED
freezer.tick(timedelta(seconds=20))
async_fire_time_changed(hass)
await hass.async_block_till_done()
await mock_scan_interval(hass, freezer)
assert entry.state is ConfigEntryState.LOADED
@@ -886,3 +891,82 @@ async def test_update_media_state(hass: HomeAssistant, client) -> None:
client.tv_state.is_on = False
await client.mock_state_update()
assert hass.states.get(ENTITY_ID).state == STATE_OFF
async def test_availability(
hass: HomeAssistant,
client,
caplog: pytest.LogCaptureFixture,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test that availability status changes are set and logged correctly."""
await setup_webostv(hass)
# Initially available
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
# Make the entity go offline - should log unavailable message
client.connect.side_effect = TimeoutError
client.is_connected.return_value = False
await mock_scan_interval(hass, freezer)
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
unavailable_log = f"LG webOS TV 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()
await mock_scan_interval(hass, freezer)
assert unavailable_log not in caplog.text
# Bring the entity back online - should log back online message
client.connect.side_effect = None
await mock_scan_interval(hass, freezer)
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
available_log = f"LG webOS TV entity {ENTITY_ID} is back online"
assert available_log in caplog.text
# Clear logs and make update again - should NOT log again
caplog.clear()
await mock_scan_interval(hass, freezer)
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
assert available_log not in caplog.text
# Test offline again to ensure the flag resets properly
client.connect.side_effect = TimeoutError
await mock_scan_interval(hass, freezer)
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
assert unavailable_log in caplog.text
# Test entity that supports turn on are considered available
assert await async_setup_component(
hass,
automation.DOMAIN,
{
automation.DOMAIN: [
{
"trigger": {
"platform": "webostv.turn_on",
"entity_id": ENTITY_ID,
},
"action": {
"service": "test.automation",
"data_template": {
"some": ENTITY_ID,
"id": "{{ trigger.id }}",
},
},
},
],
},
)
await mock_scan_interval(hass, freezer)
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
available_log = f"LG webOS TV entity {ENTITY_ID} is back online"
assert available_log in caplog.text