mirror of
https://github.com/home-assistant/core.git
synced 2026-02-15 07:36:16 +00:00
Mark entities as unavailable in Onkyo (#159521)
This commit is contained in:
@@ -115,6 +115,12 @@ async def async_setup_entry(
|
||||
if entity.enabled:
|
||||
await entity.query_state()
|
||||
|
||||
async def disconnect_callback() -> None:
|
||||
for entity in entities.values():
|
||||
if entity.enabled:
|
||||
entity.cancel_tasks()
|
||||
entity.async_write_ha_state()
|
||||
|
||||
async def update_callback(message: Status) -> None:
|
||||
if isinstance(message, status.Raw):
|
||||
return
|
||||
@@ -146,6 +152,7 @@ async def async_setup_entry(
|
||||
async_add_entities([zone_entity])
|
||||
|
||||
manager.callbacks.connect.append(connect_callback)
|
||||
manager.callbacks.disconnect.append(disconnect_callback)
|
||||
manager.callbacks.update.append(update_callback)
|
||||
|
||||
|
||||
@@ -225,13 +232,13 @@ class OnkyoMediaPlayer(MediaPlayerEntity):
|
||||
await self.query_state()
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""Cancel the tasks when the entity is removed."""
|
||||
if self._query_state_task is not None:
|
||||
self._query_state_task.cancel()
|
||||
self._query_state_task = None
|
||||
if self._query_av_info_task is not None:
|
||||
self._query_av_info_task.cancel()
|
||||
self._query_av_info_task = None
|
||||
"""Entity will be removed from hass."""
|
||||
self.cancel_tasks()
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return if entity is available."""
|
||||
return self._manager.connected
|
||||
|
||||
async def query_state(self) -> None:
|
||||
"""Query the receiver for all the info, that we care about."""
|
||||
@@ -247,6 +254,15 @@ class OnkyoMediaPlayer(MediaPlayerEntity):
|
||||
await self._manager.write(query.AudioInformation())
|
||||
await self._manager.write(query.VideoInformation())
|
||||
|
||||
def cancel_tasks(self) -> None:
|
||||
"""Cancel the tasks."""
|
||||
if self._query_state_task is not None:
|
||||
self._query_state_task.cancel()
|
||||
self._query_state_task = None
|
||||
if self._query_av_info_task is not None:
|
||||
self._query_av_info_task.cancel()
|
||||
self._query_av_info_task = None
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
"""Turn the media player on."""
|
||||
message = command.Power(self._zone, command.Power.Param.ON)
|
||||
|
||||
@@ -30,9 +30,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: todo
|
||||
reauthentication-flow:
|
||||
status: exempt
|
||||
|
||||
@@ -28,11 +28,13 @@ class Callbacks:
|
||||
"""Receiver callbacks."""
|
||||
|
||||
connect: list[Callable[[bool], Awaitable[None]]] = field(default_factory=list)
|
||||
disconnect: list[Callable[[], Awaitable[None]]] = field(default_factory=list)
|
||||
update: list[Callable[[Status], Awaitable[None]]] = field(default_factory=list)
|
||||
|
||||
def clear(self) -> None:
|
||||
"""Clear all callbacks."""
|
||||
self.connect.clear()
|
||||
self.disconnect.clear()
|
||||
self.update.clear()
|
||||
|
||||
|
||||
@@ -43,6 +45,7 @@ class ReceiverManager:
|
||||
entry: OnkyoConfigEntry
|
||||
info: ReceiverInfo
|
||||
receiver: Receiver | None = None
|
||||
connected: bool = False
|
||||
callbacks: Callbacks
|
||||
|
||||
_started: asyncio.Event
|
||||
@@ -83,6 +86,7 @@ class ReceiverManager:
|
||||
while True:
|
||||
try:
|
||||
async with connect(self.info, retry=reconnect) as self.receiver:
|
||||
self.connected = True
|
||||
if not reconnect:
|
||||
self._started.set()
|
||||
else:
|
||||
@@ -96,7 +100,9 @@ class ReceiverManager:
|
||||
reconnect = True
|
||||
|
||||
finally:
|
||||
self.connected = False
|
||||
_LOGGER.info("Disconnected: %s", self.info)
|
||||
await self.on_disconnect()
|
||||
|
||||
async def on_connect(self, reconnect: bool) -> None:
|
||||
"""Receiver (re)connected."""
|
||||
@@ -109,8 +115,13 @@ class ReceiverManager:
|
||||
for callback in self.callbacks.connect:
|
||||
await callback(reconnect)
|
||||
|
||||
async def on_disconnect(self) -> None:
|
||||
"""Receiver disconnected."""
|
||||
for callback in self.callbacks.disconnect:
|
||||
await callback()
|
||||
|
||||
async def on_update(self, message: Status) -> None:
|
||||
"""Process new message from the receiver."""
|
||||
"""New message from the receiver."""
|
||||
for callback in self.callbacks.update:
|
||||
await callback(message)
|
||||
|
||||
|
||||
@@ -76,11 +76,20 @@ async def test_reconnect(
|
||||
|
||||
assert mock_config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
manager = mock_config_entry.runtime_data.manager
|
||||
assert manager.connected is True
|
||||
|
||||
async def disconnect_assert() -> None:
|
||||
assert manager.connected is False
|
||||
|
||||
manager.callbacks.disconnect.append(disconnect_assert)
|
||||
|
||||
mock_connect.reset_mock()
|
||||
|
||||
assert mock_connect.call_count == 0
|
||||
|
||||
read_queue.put_nowait(None) # Simulate a disconnect
|
||||
# Simulate a disconnect
|
||||
read_queue.put_nowait(None)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert mock_connect.call_count == 1
|
||||
|
||||
@@ -32,6 +32,7 @@ from homeassistant.const import (
|
||||
SERVICE_VOLUME_MUTE,
|
||||
SERVICE_VOLUME_SET,
|
||||
SERVICE_VOLUME_UP,
|
||||
STATE_UNAVAILABLE,
|
||||
Platform,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
@@ -81,6 +82,30 @@ async def test_entities(
|
||||
await snapshot_platform(hass, entity_registry, snapshot, mock_config_entry.entry_id)
|
||||
|
||||
|
||||
async def test_availability(hass: HomeAssistant, read_queue: asyncio.Queue) -> None:
|
||||
"""Test entity availability on disconnect and reconnect."""
|
||||
assert (state := hass.states.get(ENTITY_ID)) is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
# Simulate a disconnect
|
||||
read_queue.put_nowait(None)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert (state := hass.states.get(ENTITY_ID)) is not None
|
||||
assert state.state == STATE_UNAVAILABLE
|
||||
|
||||
# Simulate first status update after reconnect
|
||||
read_queue.put_nowait(
|
||||
status.Power(
|
||||
Code.from_kind_zone(Kind.POWER, Zone.MAIN), None, status.Power.Param.ON
|
||||
)
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert (state := hass.states.get(ENTITY_ID)) is not None
|
||||
assert state.state != STATE_UNAVAILABLE
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("action", "action_data", "message"),
|
||||
[
|
||||
|
||||
Reference in New Issue
Block a user