1
0
mirror of https://github.com/home-assistant/core.git synced 2026-07-04 05:05:38 +01:00
Files
core/tests/components/shelly/test_media_player.py
T

757 lines
23 KiB
Python

"""Tests for Shelly media player platform."""
from copy import deepcopy
from http import HTTPStatus
from unittest.mock import Mock
from aioshelly.const import MODEL_WALL_DISPLAY
from aioshelly.exceptions import DeviceConnectionError, InvalidAuthError, RpcCallError
import pytest
from syrupy.assertion import SnapshotAssertion
from syrupy.filters import props
from homeassistant.components.media_player import (
ATTR_MEDIA_ALBUM_NAME,
ATTR_MEDIA_ARTIST,
ATTR_MEDIA_CONTENT_ID,
ATTR_MEDIA_CONTENT_TYPE,
ATTR_MEDIA_DURATION,
ATTR_MEDIA_POSITION,
ATTR_MEDIA_TITLE,
ATTR_MEDIA_VOLUME_LEVEL,
DOMAIN as MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_NEXT_TRACK,
SERVICE_MEDIA_PAUSE,
SERVICE_MEDIA_PLAY,
SERVICE_MEDIA_PREVIOUS_TRACK,
SERVICE_MEDIA_STOP,
SERVICE_PLAY_MEDIA,
SERVICE_VOLUME_SET,
)
from homeassistant.components.shelly.media_player import (
CONTENT_TYPE_AUDIO,
CONTENT_TYPE_RADIO,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
STATE_BUFFERING,
STATE_IDLE,
STATE_PLAYING,
STATE_UNAVAILABLE,
Platform,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.entity_registry import EntityRegistry
from . import init_integration, patch_platforms
from tests.typing import ClientSessionGenerator, WebSocketGenerator
ENTITY_ID = f"{MEDIA_PLAYER_DOMAIN}.test_name"
AUDIO_FILES = [
{
"album": "Album Placeholder",
"artist": "Artist Placeholder",
"duration": 106000,
"filename": "track_alpha.mp3",
"id": 16,
"index": 0,
"preview": "https://example.com/media/thumb?id=16&_t=track_alpha.mp3",
"size": 3390000,
"title": "Track Alpha",
"track": 0,
"type": "AUDIO",
"valid": True,
"year": 0,
},
{
"album": "Album Placeholder",
"artist": "Artist Placeholder",
"duration": 138000,
"filename": "track_beta.mp3",
"id": 15,
"index": 0,
"preview": "https://example.com/media/thumb?id=15&_t=track_beta.mp3",
"size": 4425000,
"title": "Track Beta",
"track": 0,
"type": "AUDIO",
"valid": True,
"year": 0,
},
{
"filename": "ringtone_gamma.mp3",
"id": 17,
"index": 0,
"preview": "https://example.com/media/thumb?id=17&_t=ringtone_gamma.mp3",
"size": 552000,
"title": "Ringtone Gamma",
"type": "RINGTONE",
"valid": True,
},
]
RADIO_STATIONS = [
{
"id": 0,
"name": "Station Alpha",
"country_code": "XX",
"icon": "https://example.com/icons/alpha.png",
},
{
"id": 1,
"name": "Station Beta",
"country_code": "XX",
"icon": "https://example.com/icons/beta.png",
},
{
"id": 2,
"name": "Station Gamma",
"country_code": "XX",
"icon": "https://example.com/icons/gamma.png",
},
{
"id": 3,
"name": "Station Delta",
"country_code": "XX",
"icon": "https://example.com/icons/delta.png",
},
]
STATUS_RADIO_STATION = {
"playback": {
"enable": True,
"buffering": False,
"volume": 5,
"media_meta": {
"thumb": "https://www.radio_station.pl/icon.png",
"title": "Radio Station",
},
"media_type": "RADIO",
},
}
STATUS_AUDIO_FILE = {
"playback": {
"buffering": False,
"enable": True,
"volume": 2,
"media_meta": {
"album": "Album Name",
"artist": "Artist",
"duration": 132415,
"position": 64644,
"thumb": (
"data:image/webp;base64,"
"UklGRkAAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAA"
"QUxQSAIAAAAAAFZQOCAYAAAAMAEAnQEqAQABAAFA"
"JiWkAANwAP79NmgA"
),
"title": "Title",
},
"media_type": "AUDIO",
}
}
@pytest.fixture(autouse=True)
def fixture_platforms():
"""Limit platforms under test."""
with patch_platforms([Platform.MEDIA_PLAYER]):
yield
async def test_rpc_media_player(
hass: HomeAssistant,
entity_registry: EntityRegistry,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
snapshot: SnapshotAssertion,
) -> None:
"""Test a Shelly RPC media player."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_RADIO_STATION
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID))
assert state == snapshot(
name=f"{ENTITY_ID}-state", exclude=props("entity_picture_local")
)
assert (entry := entity_registry.async_get(ENTITY_ID))
assert entry == snapshot(name=f"{ENTITY_ID}-entry")
monkeypatch.setitem(mock_rpc_device.status["media"]["playback"], "enable", False)
monkeypatch.setitem(mock_rpc_device.status["media"]["playback"], "buffering", True)
mock_rpc_device.mock_update()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_BUFFERING
async def test_rpc_media_player_audio_file(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test a Shelly RPC media player."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_PLAYING
assert state.attributes[ATTR_MEDIA_TITLE] == "Title"
assert state.attributes[ATTR_MEDIA_ARTIST] == "Artist"
assert state.attributes[ATTR_MEDIA_ALBUM_NAME] == "Album Name"
assert state.attributes[ATTR_MEDIA_DURATION] == 132
assert state.attributes[ATTR_MEDIA_POSITION] == 64
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.2
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_PAUSE,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
monkeypatch.setitem(mock_rpc_device.status["media"]["playback"], "enable", False)
mock_rpc_device.mock_update()
mock_rpc_device.media_play_or_pause.assert_called_once()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_IDLE
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_PLAY,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
monkeypatch.setitem(mock_rpc_device.status["media"]["playback"], "enable", True)
mock_rpc_device.mock_update()
assert len(mock_rpc_device.media_play_or_pause.mock_calls) == 2
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_PLAYING
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_STOP,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
monkeypatch.setitem(mock_rpc_device.status["media"]["playback"], "enable", False)
mock_rpc_device.mock_update()
mock_rpc_device.media_stop.assert_called_once()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_IDLE
async def test_rpc_media_player_actions(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test a Shelly RPC media player."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_NEXT_TRACK,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
mock_rpc_device.media_next.assert_called_once()
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_PREVIOUS_TRACK,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
mock_rpc_device.mock_update()
mock_rpc_device.media_previous.assert_called_once()
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_VOLUME_SET,
{ATTR_ENTITY_ID: ENTITY_ID, ATTR_MEDIA_VOLUME_LEVEL: 0.5},
blocking=True,
)
mock_rpc_device.media_set_volume.assert_called_once_with(5)
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: CONTENT_TYPE_AUDIO,
ATTR_MEDIA_CONTENT_ID: "12",
},
blocking=True,
)
mock_rpc_device.media_play_media.assert_called_once_with(12)
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: CONTENT_TYPE_RADIO,
ATTR_MEDIA_CONTENT_ID: "2",
},
blocking=True,
)
mock_rpc_device.media_play_radio_station.assert_called_once_with(2)
async def test_rpc_media_player_play_media_errors(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test a Shelly RPC errors in play media method."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
with pytest.raises(
HomeAssistantError, match="Unsupported media ID for Shelly device: invalid"
):
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: CONTENT_TYPE_RADIO,
ATTR_MEDIA_CONTENT_ID: "invalid",
},
blocking=True,
)
with pytest.raises(
HomeAssistantError, match="Unsupported media type for Shelly device: invalid"
):
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: ENTITY_ID,
ATTR_MEDIA_CONTENT_TYPE: "invalid",
ATTR_MEDIA_CONTENT_ID: "1",
},
blocking=True,
)
async def test_get_image_http(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_client_no_auth: ClientSessionGenerator,
) -> None:
"""Test get image via http command."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID)) is not None
assert "entity_picture_local" not in state.attributes
client = await hass_client_no_auth()
resp = await client.get(state.attributes["entity_picture"])
content = await resp.read()
assert isinstance(content, bytes)
@pytest.mark.parametrize(
"invalid_thumb",
[
"data:image/webp;base64,0",
"data invalid",
"data:video/mpg;base64,AAAA",
],
)
async def test_get_image_http_stale_url_after_thumb_invalidated(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_client_no_auth: ClientSessionGenerator,
invalid_thumb: str,
) -> None:
"""Test image proxy with a stale URL after the thumb becomes invalid."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID)) is not None
entity_picture = state.attributes["entity_picture"]
monkeypatch.setitem(
mock_rpc_device.status["media"]["playback"]["media_meta"],
"thumb",
invalid_thumb,
)
mock_rpc_device.mock_update()
await hass.async_block_till_done()
assert (state := hass.states.get(ENTITY_ID)) is not None
assert "entity_picture" not in state.attributes
client = await hass_client_no_auth()
resp = await client.get(entity_picture)
assert resp.status == HTTPStatus.NOT_FOUND
async def test_entity_picture_absent_base64_data_invalid(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_client: ClientSessionGenerator,
) -> None:
"""Test that entity_picture is absent when base64 data is invalid."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
status["media"]["playback"]["media_meta"]["thumb"] = "data:image/webp;base64,0"
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID)) is not None
assert "entity_picture" not in state.attributes
client = await hass_client()
resp = await client.get(f"/api/media_player_proxy/{ENTITY_ID}")
assert resp.status == HTTPStatus.NOT_FOUND
@pytest.mark.parametrize(
"invalid_thumb",
[
"data invalid",
"lorem ipsum",
],
)
async def test_entity_picture_absent_thumb_string_invalid(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_client: ClientSessionGenerator,
invalid_thumb: str,
) -> None:
"""Test that entity_picture is absent when thumb string has invalid format."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
status["media"]["playback"]["media_meta"]["thumb"] = invalid_thumb
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID)) is not None
assert "entity_picture" not in state.attributes
client = await hass_client()
resp = await client.get(f"/api/media_player_proxy/{ENTITY_ID}")
assert resp.status == HTTPStatus.NOT_FOUND
async def test_entity_picture_absent_mime_type_not_allowed(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_client: ClientSessionGenerator,
) -> None:
"""Test that entity_picture is absent when MIME type is not allowed."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
status["media"]["playback"]["media_meta"]["thumb"] = "data:video/mpg;base64,0"
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID)) is not None
assert "entity_picture" not in state.attributes
client = await hass_client()
resp = await client.get(f"/api/media_player_proxy/{ENTITY_ID}")
assert resp.status == HTTPStatus.NOT_FOUND
async def test_rpc_media_player_browse_media_root(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test Shelly media player browse media root."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json(
{
"id": 1,
"type": "media_player/browse_media",
"entity_id": ENTITY_ID,
}
)
msg = await websocket_client.receive_json()
assert msg["success"]
assert msg["result"]["title"] == "Shelly"
assert msg["result"]["media_class"] == "directory"
assert msg["result"]["media_content_id"] == ""
assert [child["title"] for child in msg["result"]["children"]] == [
"Radio stations",
"Audio files",
]
assert [child["media_content_type"] for child in msg["result"]["children"]] == [
CONTENT_TYPE_RADIO,
CONTENT_TYPE_AUDIO,
]
async def test_rpc_media_player_browse_media_radio_stations(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test Shelly media player browse media radio stations."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_RADIO_STATION
monkeypatch.setattr(mock_rpc_device, "status", status)
mock_rpc_device.media_list_radio_stations.return_value = RADIO_STATIONS
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json(
{
"id": 1,
"type": "media_player/browse_media",
"entity_id": ENTITY_ID,
"media_content_type": CONTENT_TYPE_RADIO,
"media_content_id": CONTENT_TYPE_RADIO,
}
)
msg = await websocket_client.receive_json()
assert msg["success"]
assert msg["result"]["title"] == "Radio stations"
assert msg["result"]["media_class"] == "directory"
assert msg["result"]["media_content_type"] == CONTENT_TYPE_RADIO
assert [child["title"] for child in msg["result"]["children"]] == [
station["name"] for station in RADIO_STATIONS
]
assert [child["media_content_id"] for child in msg["result"]["children"]] == [
str(station["id"]) for station in RADIO_STATIONS
]
assert [child["thumbnail"] for child in msg["result"]["children"]] == [
station["icon"] for station in RADIO_STATIONS
]
async def test_rpc_media_player_browse_media_audio_files(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test Shelly media player browse media audio files."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
mock_rpc_device.media_list_media.return_value = AUDIO_FILES
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json(
{
"id": 1,
"type": "media_player/browse_media",
"entity_id": ENTITY_ID,
"media_content_type": CONTENT_TYPE_AUDIO,
"media_content_id": CONTENT_TYPE_AUDIO,
}
)
msg = await websocket_client.receive_json()
assert msg["success"]
assert msg["result"]["title"] == "Audio files"
assert msg["result"]["media_class"] == "directory"
assert msg["result"]["media_content_type"] == CONTENT_TYPE_AUDIO
assert [child["title"] for child in msg["result"]["children"]] == [
item["title"] for item in AUDIO_FILES if item["type"] == "AUDIO"
]
assert [child["media_content_id"] for child in msg["result"]["children"]] == [
str(item["id"]) for item in AUDIO_FILES if item["type"] == "AUDIO"
]
assert [child["thumbnail"] for child in msg["result"]["children"]] == [
item["preview"] for item in AUDIO_FILES if item["type"] == "AUDIO"
]
async def test_rpc_media_player_browse_media_unsupported_media_type(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_ws_client: WebSocketGenerator,
) -> None:
"""Test Shelly media player browse media returns unsupported media content type."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
mock_rpc_device.media_list_media.return_value = AUDIO_FILES
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json(
{
"id": 1,
"type": "media_player/browse_media",
"entity_id": ENTITY_ID,
"media_content_type": "invalid",
"media_content_id": CONTENT_TYPE_AUDIO,
}
)
msg = await websocket_client.receive_json()
assert msg["error"]
assert msg["error"]["code"] == "home_assistant_error"
assert msg["error"]["message"] == (
"Unsupported media content type for Shelly device: invalid"
)
@pytest.mark.parametrize(
("side_effect", "expected_message"),
[
(
DeviceConnectionError,
"Device communication error occurred while calling action"
" for media_player.test_name of Test name",
),
(
RpcCallError(999),
"RPC call error occurred while calling action"
" for media_player.test_name of Test name",
),
(
InvalidAuthError,
"Authentication failed for Test name, please update your credentials",
),
],
)
async def test_rpc_media_player_browse_media_errors(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
hass_ws_client: WebSocketGenerator,
side_effect: Exception,
expected_message: str,
) -> None:
"""Test Shelly media player browse media returns errors."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
mock_rpc_device.media_list_media.side_effect = side_effect
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
websocket_client = await hass_ws_client(hass)
await websocket_client.send_json(
{
"id": 1,
"type": "media_player/browse_media",
"entity_id": ENTITY_ID,
"media_content_type": CONTENT_TYPE_AUDIO,
"media_content_id": CONTENT_TYPE_AUDIO,
}
)
msg = await websocket_client.receive_json()
assert msg["error"]
assert msg["error"]["code"] == "home_assistant_error"
assert msg["error"]["message"] == expected_message
async def test_rpc_media_player_no_media_meta(
hass: HomeAssistant,
entity_registry: EntityRegistry,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
snapshot: SnapshotAssertion,
) -> None:
"""Test a Shelly RPC media player with no media metadata."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
status["media"]["playback"].pop("media_meta")
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_PLAYING
assert state.attributes.get(ATTR_MEDIA_TITLE) is None
assert state.attributes.get(ATTR_MEDIA_ARTIST) is None
assert state.attributes.get(ATTR_MEDIA_ALBUM_NAME) is None
assert state.attributes.get(ATTR_MEDIA_DURATION) is None
assert state.attributes.get(ATTR_MEDIA_POSITION) is None
async def test_rpc_media_player_unavailable(
hass: HomeAssistant,
mock_rpc_device: Mock,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""Test media player entity handles device going offline without raising."""
status = deepcopy(mock_rpc_device.status)
status["media"] = STATUS_AUDIO_FILE
monkeypatch.setattr(mock_rpc_device, "status", status)
await init_integration(hass, 2, model=MODEL_WALL_DISPLAY)
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_PLAYING
monkeypatch.setattr(mock_rpc_device, "connected", False)
monkeypatch.setattr(mock_rpc_device, "initialized", False)
mock_rpc_device.mock_disconnected()
await hass.async_block_till_done()
assert (state := hass.states.get(ENTITY_ID))
assert state.state == STATE_UNAVAILABLE