diff --git a/homeassistant/components/media_player/__init__.py b/homeassistant/components/media_player/__init__.py index 57167317553..f3d4321e751 100644 --- a/homeassistant/components/media_player/__init__.py +++ b/homeassistant/components/media_player/__init__.py @@ -6,6 +6,7 @@ https://home-assistant.io/components/media_player/ """ import logging import os +import requests import voluptuous as vol @@ -13,6 +14,7 @@ from homeassistant.config import load_yaml_config_file from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa +from homeassistant.components.http import HomeAssistantView import homeassistant.helpers.config_validation as cv from homeassistant.const import ( STATE_OFF, STATE_UNKNOWN, STATE_PLAYING, STATE_IDLE, @@ -25,10 +27,13 @@ from homeassistant.const import ( _LOGGER = logging.getLogger(__name__) DOMAIN = 'media_player' +DEPENDENCIES = ['http'] SCAN_INTERVAL = 10 ENTITY_ID_FORMAT = DOMAIN + '.{}' +ENTITY_IMAGE_URL = '/api/media_player_proxy/{0}?token={1}' + SERVICE_PLAY_MEDIA = 'play_media' SERVICE_SELECT_SOURCE = 'select_source' SERVICE_CLEAR_PLAYLIST = 'clear_playlist' @@ -286,6 +291,8 @@ def setup(hass, config): component = EntityComponent( logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) + hass.wsgi.register_view(MediaPlayerImageView(hass, component.entities)) + component.setup(config) descriptions = load_yaml_config_file( @@ -398,6 +405,11 @@ class MediaPlayerDevice(Entity): """State of the player.""" return STATE_UNKNOWN + @property + def access_token(self): + """Access token for this media player.""" + return str(id(self)) + @property def volume_level(self): """Volume level of the media player (0..1).""" @@ -633,7 +645,8 @@ class MediaPlayerDevice(Entity): @property def entity_picture(self): """Return image of the media playing.""" - return None if self.state == STATE_OFF else self.media_image_url + return None if self.state == STATE_OFF else \ + ENTITY_IMAGE_URL.format(self.entity_id, self.access_token) @property def state_attributes(self): @@ -649,3 +662,35 @@ class MediaPlayerDevice(Entity): } return state_attr + + +class MediaPlayerImageView(HomeAssistantView): + """Media player view to serve an image.""" + + url = "/api/media_player_proxy/" + name = "api:media_player:image" + + def __init__(self, hass, entities): + """Initialize a media player view.""" + super().__init__(hass) + self.entities = entities + + def get(self, request, entity_id): + """Start a get request.""" + player = self.entities.get(entity_id) + + if player is None: + return self.Response(status=404) + + authenticated = (request.authenticated or + request.args.get('token') == player.access_token) + + if not authenticated: + return self.Response(status=401) + + response = requests.get(player.media_image_url) + + if response is None: + return self.Response(status=500) + + return self.Response(response) diff --git a/tests/components/media_player/test_demo.py b/tests/components/media_player/test_demo.py index aa7350ff930..f2b9ce4b032 100644 --- a/tests/components/media_player/test_demo.py +++ b/tests/components/media_player/test_demo.py @@ -1,23 +1,50 @@ """The tests for the Demo Media player platform.""" import unittest from unittest.mock import patch +from homeassistant import bootstrap import homeassistant.components.media_player as mp +import homeassistant.components.http as http -from tests.common import get_test_home_assistant +import requests +import requests_mock +import time + +from tests.common import get_test_home_assistant, get_test_instance_port + +SERVER_PORT = get_test_instance_port() +HTTP_BASE_URL = 'http://127.0.0.1:{}'.format(SERVER_PORT) + +hass = None entity_id = 'media_player.walkman' +def setUpModule(): # pylint: disable=invalid-name + """Initalize a Home Assistant server.""" + global hass + + hass = get_test_home_assistant() + bootstrap.setup_component(hass, http.DOMAIN, { + http.DOMAIN: { + http.CONF_SERVER_PORT: SERVER_PORT + }, + }) + + hass.start() + time.sleep(0.05) + + +def tearDownModule(): # pylint: disable=invalid-name + """Stop the Home Assistant server.""" + hass.stop() + + class TestDemoMediaPlayer(unittest.TestCase): """Test the media_player module.""" def setUp(self): # pylint: disable=invalid-name """Setup things to be run when tests are started.""" - self.hass = get_test_home_assistant() - - def tearDown(self): # pylint: disable=invalid-name - """Stop everything that was started.""" - self.hass.stop() + self.hass = hass def test_source_select(self): """Test the input source service.""" @@ -175,6 +202,19 @@ class TestDemoMediaPlayer(unittest.TestCase): assert 0 == (mp.SUPPORT_PREVIOUS_TRACK & state.attributes.get('supported_media_commands')) + @requests_mock.Mocker(real_http=True) + def test_media_image_proxy(self, m): + """Test the media server image proxy server .""" + fake_picture_data = 'test.test' + m.get('https://graph.facebook.com/v2.5/107771475912710/' + 'picture?type=large', text=fake_picture_data) + assert mp.setup(self.hass, {'media_player': {'platform': 'demo'}}) + assert self.hass.states.is_state(entity_id, 'playing') + state = self.hass.states.get(entity_id) + req = requests.get(HTTP_BASE_URL + + state.attributes.get('entity_picture')) + assert req.text == fake_picture_data + @patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.' 'media_seek') def test_play_media(self, mock_seek):