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 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/<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."""
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):