Proxy requests to the media player's media image (#2693)

This is needed when the media server and UI client are not on the same network.
This commit is contained in:
Assaf Inbal 2016-08-03 04:31:15 +03:00 committed by Paulus Schoutsen
parent e36c6b24ee
commit bb22ad3064
2 changed files with 92 additions and 7 deletions

View file

@ -6,6 +6,7 @@ https://home-assistant.io/components/media_player/
""" """
import logging import logging
import os import os
import requests
import voluptuous as vol 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 import Entity
from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.entity_component import EntityComponent
from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa from homeassistant.helpers.config_validation import PLATFORM_SCHEMA # noqa
from homeassistant.components.http import HomeAssistantView
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.const import ( from homeassistant.const import (
STATE_OFF, STATE_UNKNOWN, STATE_PLAYING, STATE_IDLE, STATE_OFF, STATE_UNKNOWN, STATE_PLAYING, STATE_IDLE,
@ -25,10 +27,13 @@ from homeassistant.const import (
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
DOMAIN = 'media_player' DOMAIN = 'media_player'
DEPENDENCIES = ['http']
SCAN_INTERVAL = 10 SCAN_INTERVAL = 10
ENTITY_ID_FORMAT = DOMAIN + '.{}' ENTITY_ID_FORMAT = DOMAIN + '.{}'
ENTITY_IMAGE_URL = '/api/media_player_proxy/{0}?token={1}'
SERVICE_PLAY_MEDIA = 'play_media' SERVICE_PLAY_MEDIA = 'play_media'
SERVICE_SELECT_SOURCE = 'select_source' SERVICE_SELECT_SOURCE = 'select_source'
SERVICE_CLEAR_PLAYLIST = 'clear_playlist' SERVICE_CLEAR_PLAYLIST = 'clear_playlist'
@ -286,6 +291,8 @@ def setup(hass, config):
component = EntityComponent( component = EntityComponent(
logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL) logging.getLogger(__name__), DOMAIN, hass, SCAN_INTERVAL)
hass.wsgi.register_view(MediaPlayerImageView(hass, component.entities))
component.setup(config) component.setup(config)
descriptions = load_yaml_config_file( descriptions = load_yaml_config_file(
@ -398,6 +405,11 @@ class MediaPlayerDevice(Entity):
"""State of the player.""" """State of the player."""
return STATE_UNKNOWN return STATE_UNKNOWN
@property
def access_token(self):
"""Access token for this media player."""
return str(id(self))
@property @property
def volume_level(self): def volume_level(self):
"""Volume level of the media player (0..1).""" """Volume level of the media player (0..1)."""
@ -633,7 +645,8 @@ class MediaPlayerDevice(Entity):
@property @property
def entity_picture(self): def entity_picture(self):
"""Return image of the media playing.""" """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 @property
def state_attributes(self): def state_attributes(self):
@ -649,3 +662,35 @@ class MediaPlayerDevice(Entity):
} }
return state_attr return state_attr
class MediaPlayerImageView(HomeAssistantView):
"""Media player view to serve an image."""
url = "/api/media_player_proxy/<entity(domain=media_player):entity_id>"
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)

View file

@ -1,23 +1,50 @@
"""The tests for the Demo Media player platform.""" """The tests for the Demo Media player platform."""
import unittest import unittest
from unittest.mock import patch from unittest.mock import patch
from homeassistant import bootstrap
import homeassistant.components.media_player as mp 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' 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): class TestDemoMediaPlayer(unittest.TestCase):
"""Test the media_player module.""" """Test the media_player module."""
def setUp(self): # pylint: disable=invalid-name def setUp(self): # pylint: disable=invalid-name
"""Setup things to be run when tests are started.""" """Setup things to be run when tests are started."""
self.hass = get_test_home_assistant() self.hass = hass
def tearDown(self): # pylint: disable=invalid-name
"""Stop everything that was started."""
self.hass.stop()
def test_source_select(self): def test_source_select(self):
"""Test the input source service.""" """Test the input source service."""
@ -175,6 +202,19 @@ class TestDemoMediaPlayer(unittest.TestCase):
assert 0 == (mp.SUPPORT_PREVIOUS_TRACK & assert 0 == (mp.SUPPORT_PREVIOUS_TRACK &
state.attributes.get('supported_media_commands')) 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.' @patch('homeassistant.components.media_player.demo.DemoYoutubePlayer.'
'media_seek') 'media_seek')
def test_play_media(self, mock_seek): def test_play_media(self, mock_seek):