diff --git a/.coveragerc b/.coveragerc index 237571a212f..44ff59bbbde 100644 --- a/.coveragerc +++ b/.coveragerc @@ -314,7 +314,6 @@ omit = homeassistant/components/esphome/entry_data.py homeassistant/components/esphome/light.py homeassistant/components/esphome/lock.py - homeassistant/components/esphome/media_player.py homeassistant/components/esphome/number.py homeassistant/components/esphome/switch.py homeassistant/components/etherscan/sensor.py diff --git a/tests/components/esphome/test_media_player.py b/tests/components/esphome/test_media_player.py new file mode 100644 index 00000000000..bcef78e9345 --- /dev/null +++ b/tests/components/esphome/test_media_player.py @@ -0,0 +1,250 @@ +"""Test ESPHome media_players.""" + +from unittest.mock import AsyncMock, Mock, call + +from aioesphomeapi import ( + APIClient, + MediaPlayerCommand, + MediaPlayerEntityState, + MediaPlayerInfo, + MediaPlayerState, +) +import pytest + +from homeassistant.components import media_source +from homeassistant.components.media_player import ( + ATTR_MEDIA_CONTENT_ID, + ATTR_MEDIA_CONTENT_TYPE, + ATTR_MEDIA_VOLUME_LEVEL, + ATTR_MEDIA_VOLUME_MUTED, + DOMAIN as MEDIA_PLAYER_DOMAIN, + SERVICE_MEDIA_PAUSE, + SERVICE_MEDIA_PLAY, + SERVICE_MEDIA_STOP, + SERVICE_PLAY_MEDIA, + SERVICE_VOLUME_MUTE, + SERVICE_VOLUME_SET, + BrowseMedia, + MediaClass, + MediaType, +) +from homeassistant.const import ATTR_ENTITY_ID +from homeassistant.core import HomeAssistant +from homeassistant.setup import async_setup_component + +from tests.common import ( + mock_platform, +) +from tests.typing import WebSocketGenerator + + +async def test_media_player_entity( + hass: HomeAssistant, mock_client: APIClient, mock_generic_device_entry +) -> None: + """Test a generic media_player entity.""" + entity_info = [ + MediaPlayerInfo( + object_id="mymedia_player", + key=1, + name="my media_player", + unique_id="my_media_player", + supports_pause=True, + ) + ] + states = [ + MediaPlayerEntityState( + key=1, volume=50, muted=True, state=MediaPlayerState.PAUSED + ) + ] + user_service = [] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + user_service=user_service, + states=states, + ) + state = hass.states.get("media_player.test_my_media_player") + assert state is not None + assert state.state == "paused" + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_VOLUME_MUTE, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + ATTR_MEDIA_VOLUME_MUTED: True, + }, + blocking=True, + ) + mock_client.media_player_command.assert_has_calls( + [call(1, command=MediaPlayerCommand.MUTE)] + ) + mock_client.media_player_command.reset_mock() + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_VOLUME_MUTE, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + ATTR_MEDIA_VOLUME_MUTED: True, + }, + blocking=True, + ) + mock_client.media_player_command.assert_has_calls( + [call(1, command=MediaPlayerCommand.MUTE)] + ) + mock_client.media_player_command.reset_mock() + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_VOLUME_SET, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + ATTR_MEDIA_VOLUME_LEVEL: 0.5, + }, + blocking=True, + ) + mock_client.media_player_command.assert_has_calls([call(1, volume=0.5)]) + mock_client.media_player_command.reset_mock() + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_MEDIA_PAUSE, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + }, + blocking=True, + ) + mock_client.media_player_command.assert_has_calls( + [call(1, command=MediaPlayerCommand.PAUSE)] + ) + mock_client.media_player_command.reset_mock() + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_MEDIA_PLAY, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + }, + blocking=True, + ) + mock_client.media_player_command.assert_has_calls( + [call(1, command=MediaPlayerCommand.PLAY)] + ) + mock_client.media_player_command.reset_mock() + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_MEDIA_STOP, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + }, + blocking=True, + ) + mock_client.media_player_command.assert_has_calls( + [call(1, command=MediaPlayerCommand.STOP)] + ) + mock_client.media_player_command.reset_mock() + + +async def test_media_player_entity_with_source( + hass: HomeAssistant, + mock_client: APIClient, + hass_ws_client: WebSocketGenerator, + mock_generic_device_entry, +) -> None: + """Test a generic media_player entity media source.""" + esphome_platform_mock = Mock( + async_get_media_browser_root_object=AsyncMock( + return_value=[ + BrowseMedia( + title="Spotify", + media_class=MediaClass.APP, + media_content_id="", + media_content_type="spotify", + thumbnail="https://brands.home-assistant.io/_/spotify/logo.png", + can_play=False, + can_expand=True, + ) + ] + ), + async_browse_media=AsyncMock( + return_value=BrowseMedia( + title="Spotify Favourites", + media_class=MediaClass.PLAYLIST, + media_content_id="", + media_content_type="spotify", + can_play=True, + can_expand=False, + ) + ), + async_play_media=AsyncMock(return_value=False), + ) + mock_platform(hass, "test.esphome", esphome_platform_mock) + await async_setup_component(hass, "test", {"test": {}}) + await async_setup_component(hass, "media_source", {"media_source": {}}) + await hass.async_block_till_done() + + entity_info = [ + MediaPlayerInfo( + object_id="mymedia_player", + key=1, + name="my media_player", + unique_id="my_media_player", + supports_pause=True, + ) + ] + states = [ + MediaPlayerEntityState( + key=1, volume=50, muted=True, state=MediaPlayerState.PLAYING + ) + ] + user_service = [] + await mock_generic_device_entry( + mock_client=mock_client, + entity_info=entity_info, + user_service=user_service, + states=states, + ) + state = hass.states.get("media_player.test_my_media_player") + assert state is not None + assert state.state == "playing" + + with pytest.raises(media_source.error.Unresolvable): + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_PLAY_MEDIA, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC, + ATTR_MEDIA_CONTENT_ID: "media-source://local/xz", + }, + blocking=True, + ) + + mock_client.media_player_command.reset_mock() + client = await hass_ws_client() + await client.send_json( + { + "id": 1, + "type": "media_player/browse_media", + "entity_id": "media_player.test_my_media_player", + } + ) + response = await client.receive_json() + assert response["success"] + + await hass.services.async_call( + MEDIA_PLAYER_DOMAIN, + SERVICE_PLAY_MEDIA, + { + ATTR_ENTITY_ID: "media_player.test_my_media_player", + ATTR_MEDIA_CONTENT_TYPE: MediaType.URL, + ATTR_MEDIA_CONTENT_ID: "media-source://tts?message=hello", + }, + blocking=True, + ) + + mock_client.media_player_command.assert_has_calls( + [call(1, media_url="media-source://tts?message=hello")] + )