Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 07b6358

Browse files
authored
Fix LG webOS TV entity availability status (home-assistant#155164)
1 parent 6fa73f7 commit 07b6358

File tree

3 files changed

+115
-18
lines changed

3 files changed

+115
-18
lines changed

‎homeassistant/components/webostv/media_player.py‎

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def __init__(self, entry: WebOsTvConfigEntry) -> None:
162162
self._entry = entry
163163
self._client = entry.runtime_data
164164
self._attr_assumed_state = True
165+
self._unavailable_logged = False
165166
self._device_name = entry.title
166167
self._attr_unique_id = entry.unique_id
167168
self._sources = entry.options.get(CONF_SOURCES)
@@ -348,19 +349,31 @@ def _update_sources(self) -> None:
348349
):
349350
self._source_list["Live TV"] = app
350351

352+
def _set_availability(self, available: bool) -> None:
353+
"""Set availability and log changes only once."""
354+
self._attr_available = available
355+
if not available and not self._unavailable_logged:
356+
_LOGGER.info("LG webOS TV entity %s is unavailable", self.entity_id)
357+
self._unavailable_logged = True
358+
elif available and self._unavailable_logged:
359+
_LOGGER.info("LG webOS TV entity %s is back online", self.entity_id)
360+
self._unavailable_logged = False
361+
351362
@util.Throttle(MIN_TIME_BETWEEN_SCANS, MIN_TIME_BETWEEN_FORCED_SCANS)
352363
async def async_update(self) -> None:
353364
"""Connect."""
354365
if self._client.is_connected():
355366
return
356367

357-
with suppress(*WEBOSTV_EXCEPTIONS):
358-
try:
359-
await self._client.connect()
360-
except WebOsTvPairError:
361-
self._entry.async_start_reauth(self.hass)
362-
else:
363-
update_client_key(self.hass, self._entry)
368+
try:
369+
await self._client.connect()
370+
except WEBOSTV_EXCEPTIONS:
371+
self._set_availability(bool(self._turn_on))
372+
except WebOsTvPairError:
373+
self._entry.async_start_reauth(self.hass)
374+
else:
375+
self._set_availability(True)
376+
update_client_key(self.hass, self._entry)
364377

365378
@property
366379
def supported_features(self) -> MediaPlayerEntityFeature:

‎homeassistant/components/webostv/quality_scale.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ rules:
2626
config-entry-unloading: done
2727
docs-configuration-parameters: done
2828
docs-installation-parameters: done
29-
entity-unavailable: todo
29+
entity-unavailable: done
3030
integration-owner: done
31-
log-when-unavailable: todo
31+
log-when-unavailable: done
3232
parallel-updates: done
3333
reauthentication-flow: done
3434
test-coverage: done

‎tests/components/webostv/test_media_player.py‎

Lines changed: 93 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
SERVICE_VOLUME_SET,
6060
SERVICE_VOLUME_UP,
6161
STATE_OFF,
62+
STATE_UNAVAILABLE,
6263
)
6364
from homeassistant.core import HomeAssistant, State
6465
from homeassistant.exceptions import HomeAssistantError
@@ -73,6 +74,16 @@
7374
from tests.typing import ClientSessionGenerator
7475

7576

77+
async def mock_scan_interval(
78+
hass: HomeAssistant,
79+
freezer: FrozenDateTimeFactory,
80+
) -> None:
81+
"""Mock update interval to force an update."""
82+
freezer.tick(timedelta(seconds=11))
83+
async_fire_time_changed(hass)
84+
await hass.async_block_till_done()
85+
86+
7687
@pytest.mark.parametrize(
7788
("service", "attr_data", "client_call"),
7889
[
@@ -488,9 +499,7 @@ async def test_client_disconnected(
488499
client.is_connected.return_value = False
489500
client.connect.side_effect = TimeoutError
490501

491-
freezer.tick(timedelta(seconds=20))
492-
async_fire_time_changed(hass)
493-
await hass.async_block_till_done()
502+
await mock_scan_interval(hass, freezer)
494503

495504
assert "TimeoutError" not in caplog.text
496505

@@ -506,9 +515,7 @@ async def test_client_key_update_on_connect(
506515
client.is_connected.return_value = False
507516
client.client_key = "new_key"
508517

509-
freezer.tick(timedelta(seconds=20))
510-
async_fire_time_changed(hass)
511-
await hass.async_block_till_done()
518+
await mock_scan_interval(hass, freezer)
512519

513520
assert config_entry.data[CONF_CLIENT_SECRET] == client.client_key
514521

@@ -849,9 +856,7 @@ async def test_reauth_reconnect(
849856

850857
assert entry.state is ConfigEntryState.LOADED
851858

852-
freezer.tick(timedelta(seconds=20))
853-
async_fire_time_changed(hass)
854-
await hass.async_block_till_done()
859+
await mock_scan_interval(hass, freezer)
855860

856861
assert entry.state is ConfigEntryState.LOADED
857862

@@ -886,3 +891,82 @@ async def test_update_media_state(hass: HomeAssistant, client) -> None:
886891
client.tv_state.is_on = False
887892
await client.mock_state_update()
888893
assert hass.states.get(ENTITY_ID).state == STATE_OFF
894+
895+
896+
async def test_availability(
897+
hass: HomeAssistant,
898+
client,
899+
caplog: pytest.LogCaptureFixture,
900+
freezer: FrozenDateTimeFactory,
901+
) -> None:
902+
"""Test that availability status changes are set and logged correctly."""
903+
await setup_webostv(hass)
904+
905+
# Initially available
906+
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
907+
908+
# Make the entity go offline - should log unavailable message
909+
client.connect.side_effect = TimeoutError
910+
client.is_connected.return_value = False
911+
await mock_scan_interval(hass, freezer)
912+
913+
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
914+
unavailable_log = f"LG webOS TV entity {ENTITY_ID} is unavailable"
915+
assert unavailable_log in caplog.text
916+
917+
# Clear logs and update the offline entity again - should NOT log again
918+
caplog.clear()
919+
await mock_scan_interval(hass, freezer)
920+
921+
assert unavailable_log not in caplog.text
922+
923+
# Bring the entity back online - should log back online message
924+
client.connect.side_effect = None
925+
await mock_scan_interval(hass, freezer)
926+
927+
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
928+
available_log = f"LG webOS TV entity {ENTITY_ID} is back online"
929+
assert available_log in caplog.text
930+
931+
# Clear logs and make update again - should NOT log again
932+
caplog.clear()
933+
await mock_scan_interval(hass, freezer)
934+
935+
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
936+
assert available_log not in caplog.text
937+
938+
# Test offline again to ensure the flag resets properly
939+
client.connect.side_effect = TimeoutError
940+
await mock_scan_interval(hass, freezer)
941+
942+
assert hass.states.get(ENTITY_ID).state == STATE_UNAVAILABLE
943+
assert unavailable_log in caplog.text
944+
945+
# Test entity that supports turn on are considered available
946+
assert await async_setup_component(
947+
hass,
948+
automation.DOMAIN,
949+
{
950+
automation.DOMAIN: [
951+
{
952+
"trigger": {
953+
"platform": "webostv.turn_on",
954+
"entity_id": ENTITY_ID,
955+
},
956+
"action": {
957+
"service": "test.automation",
958+
"data_template": {
959+
"some": ENTITY_ID,
960+
"id": "{{ trigger.id }}",
961+
},
962+
},
963+
},
964+
],
965+
},
966+
)
967+
968+
await mock_scan_interval(hass, freezer)
969+
970+
assert hass.states.get(ENTITY_ID).state == MediaPlayerState.ON
971+
available_log = f"LG webOS TV entity {ENTITY_ID} is back online"
972+
assert available_log in caplog.text

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /