mirror of
https://github.com/home-assistant/core.git
synced 2025-07-16 17:57:11 +00:00
Add useragent-based detection of JS version (#10776)
* Add useragent-based detection of JS version * Keep es5 as default meanwhile * Update test
This commit is contained in:
parent
7ab15c0e79
commit
99ea2c17a1
@ -23,7 +23,7 @@ from homeassistant.const import CONF_NAME, EVENT_THEMES_UPDATED
|
|||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.loader import bind_hass
|
from homeassistant.loader import bind_hass
|
||||||
|
|
||||||
REQUIREMENTS = ['home-assistant-frontend==20171127.0']
|
REQUIREMENTS = ['home-assistant-frontend==20171127.0', 'user-agents==1.1.0']
|
||||||
|
|
||||||
DOMAIN = 'frontend'
|
DOMAIN = 'frontend'
|
||||||
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log']
|
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log']
|
||||||
@ -32,6 +32,7 @@ URL_PANEL_COMPONENT_FP = '/frontend/panels/{}-{}.html'
|
|||||||
|
|
||||||
CONF_THEMES = 'themes'
|
CONF_THEMES = 'themes'
|
||||||
CONF_EXTRA_HTML_URL = 'extra_html_url'
|
CONF_EXTRA_HTML_URL = 'extra_html_url'
|
||||||
|
CONF_EXTRA_HTML_URL_ES5 = 'extra_html_url_es5'
|
||||||
CONF_FRONTEND_REPO = 'development_repo'
|
CONF_FRONTEND_REPO = 'development_repo'
|
||||||
CONF_JS_VERSION = 'javascript_version'
|
CONF_JS_VERSION = 'javascript_version'
|
||||||
JS_DEFAULT_OPTION = 'es5'
|
JS_DEFAULT_OPTION = 'es5'
|
||||||
@ -63,6 +64,7 @@ DATA_FINALIZE_PANEL = 'frontend_finalize_panel'
|
|||||||
DATA_PANELS = 'frontend_panels'
|
DATA_PANELS = 'frontend_panels'
|
||||||
DATA_JS_VERSION = 'frontend_js_version'
|
DATA_JS_VERSION = 'frontend_js_version'
|
||||||
DATA_EXTRA_HTML_URL = 'frontend_extra_html_url'
|
DATA_EXTRA_HTML_URL = 'frontend_extra_html_url'
|
||||||
|
DATA_EXTRA_HTML_URL_ES5 = 'frontend_extra_html_url_es5'
|
||||||
DATA_THEMES = 'frontend_themes'
|
DATA_THEMES = 'frontend_themes'
|
||||||
DATA_DEFAULT_THEME = 'frontend_default_theme'
|
DATA_DEFAULT_THEME = 'frontend_default_theme'
|
||||||
DEFAULT_THEME = 'default'
|
DEFAULT_THEME = 'default'
|
||||||
@ -79,6 +81,8 @@ CONFIG_SCHEMA = vol.Schema({
|
|||||||
}),
|
}),
|
||||||
vol.Optional(CONF_EXTRA_HTML_URL):
|
vol.Optional(CONF_EXTRA_HTML_URL):
|
||||||
vol.All(cv.ensure_list, [cv.string]),
|
vol.All(cv.ensure_list, [cv.string]),
|
||||||
|
vol.Optional(CONF_EXTRA_HTML_URL_ES5):
|
||||||
|
vol.All(cv.ensure_list, [cv.string]),
|
||||||
vol.Optional(CONF_JS_VERSION, default=JS_DEFAULT_OPTION):
|
vol.Optional(CONF_JS_VERSION, default=JS_DEFAULT_OPTION):
|
||||||
vol.In(JS_OPTIONS)
|
vol.In(JS_OPTIONS)
|
||||||
}),
|
}),
|
||||||
@ -269,11 +273,12 @@ def async_register_panel(hass, component_name, path, md5=None,
|
|||||||
|
|
||||||
@bind_hass
|
@bind_hass
|
||||||
@callback
|
@callback
|
||||||
def add_extra_html_url(hass, url):
|
def add_extra_html_url(hass, url, es5=False):
|
||||||
"""Register extra html url to load."""
|
"""Register extra html url to load."""
|
||||||
url_set = hass.data.get(DATA_EXTRA_HTML_URL)
|
key = DATA_EXTRA_HTML_URL_ES5 if es5 else DATA_EXTRA_HTML_URL
|
||||||
|
url_set = hass.data.get(key)
|
||||||
if url_set is None:
|
if url_set is None:
|
||||||
url_set = hass.data[DATA_EXTRA_HTML_URL] = set()
|
url_set = hass.data[key] = set()
|
||||||
url_set.add(url)
|
url_set.add(url)
|
||||||
|
|
||||||
|
|
||||||
@ -358,9 +363,13 @@ def async_setup(hass, config):
|
|||||||
|
|
||||||
if DATA_EXTRA_HTML_URL not in hass.data:
|
if DATA_EXTRA_HTML_URL not in hass.data:
|
||||||
hass.data[DATA_EXTRA_HTML_URL] = set()
|
hass.data[DATA_EXTRA_HTML_URL] = set()
|
||||||
|
if DATA_EXTRA_HTML_URL_ES5 not in hass.data:
|
||||||
|
hass.data[DATA_EXTRA_HTML_URL_ES5] = set()
|
||||||
|
|
||||||
for url in conf.get(CONF_EXTRA_HTML_URL, []):
|
for url in conf.get(CONF_EXTRA_HTML_URL, []):
|
||||||
add_extra_html_url(hass, url)
|
add_extra_html_url(hass, url, False)
|
||||||
|
for url in conf.get(CONF_EXTRA_HTML_URL_ES5, []):
|
||||||
|
add_extra_html_url(hass, url, True)
|
||||||
|
|
||||||
yield from async_setup_themes(hass, conf.get(CONF_THEMES))
|
yield from async_setup_themes(hass, conf.get(CONF_THEMES))
|
||||||
|
|
||||||
@ -488,12 +497,14 @@ class IndexView(HomeAssistantView):
|
|||||||
|
|
||||||
template = yield from hass.async_add_job(self.get_template, latest)
|
template = yield from hass.async_add_job(self.get_template, latest)
|
||||||
|
|
||||||
|
extra_key = DATA_EXTRA_HTML_URL if latest else DATA_EXTRA_HTML_URL_ES5
|
||||||
|
|
||||||
resp = template.render(
|
resp = template.render(
|
||||||
no_auth=no_auth,
|
no_auth=no_auth,
|
||||||
panel_url=panel_url,
|
panel_url=panel_url,
|
||||||
panels=hass.data[DATA_PANELS],
|
panels=hass.data[DATA_PANELS],
|
||||||
theme_color=MANIFEST_JSON['theme_color'],
|
theme_color=MANIFEST_JSON['theme_color'],
|
||||||
extra_urls=hass.data[DATA_EXTRA_HTML_URL],
|
extra_urls=hass.data[extra_key],
|
||||||
)
|
)
|
||||||
|
|
||||||
return web.Response(text=resp, content_type='text/html')
|
return web.Response(text=resp, content_type='text/html')
|
||||||
@ -545,10 +556,36 @@ def _is_latest(js_option, request):
|
|||||||
"""
|
"""
|
||||||
if request is None:
|
if request is None:
|
||||||
return js_option == 'latest'
|
return js_option == 'latest'
|
||||||
latest_in_query = 'latest' in request.query or (
|
|
||||||
request.headers.get('Referer') and
|
# latest in query
|
||||||
'latest' in urlparse(request.headers['Referer']).query)
|
if 'latest' in request.query or (
|
||||||
es5_in_query = 'es5' in request.query or (
|
request.headers.get('Referer') and
|
||||||
request.headers.get('Referer') and
|
'latest' in urlparse(request.headers['Referer']).query):
|
||||||
'es5' in urlparse(request.headers['Referer']).query)
|
return True
|
||||||
return latest_in_query or (not es5_in_query and js_option == 'latest')
|
|
||||||
|
# es5 in query
|
||||||
|
if 'es5' in request.query or (
|
||||||
|
request.headers.get('Referer') and
|
||||||
|
'es5' in urlparse(request.headers['Referer']).query):
|
||||||
|
return False
|
||||||
|
|
||||||
|
# non-auto option in config
|
||||||
|
if js_option != 'auto':
|
||||||
|
return js_option == 'latest'
|
||||||
|
|
||||||
|
from user_agents import parse
|
||||||
|
useragent = parse(request.headers.get('User-Agent'))
|
||||||
|
|
||||||
|
# on iOS every browser is a Safari which we support from version 10.
|
||||||
|
if useragent.os.family == 'iOS':
|
||||||
|
return useragent.os.version[0] >= 10
|
||||||
|
|
||||||
|
family_min_version = {
|
||||||
|
'Chrome': 50, # Probably can reduce this
|
||||||
|
'Firefox': 41, # Destructuring added in 41
|
||||||
|
'Opera': 40, # Probably can reduce this
|
||||||
|
'Edge': 14, # Maybe can reduce this
|
||||||
|
'Safari': 10, # many features not supported by 9
|
||||||
|
}
|
||||||
|
version = family_min_version.get(useragent.browser.family)
|
||||||
|
return version and useragent.browser.version[0] >= version
|
||||||
|
@ -1100,6 +1100,9 @@ uber_rides==0.6.0
|
|||||||
# homeassistant.components.sensor.ups
|
# homeassistant.components.sensor.ups
|
||||||
upsmychoice==1.0.6
|
upsmychoice==1.0.6
|
||||||
|
|
||||||
|
# homeassistant.components.frontend
|
||||||
|
user-agents==1.1.0
|
||||||
|
|
||||||
# homeassistant.components.camera.uvc
|
# homeassistant.components.camera.uvc
|
||||||
uvcclient==0.10.1
|
uvcclient==0.10.1
|
||||||
|
|
||||||
|
@ -7,7 +7,8 @@ import pytest
|
|||||||
|
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.components.frontend import (
|
from homeassistant.components.frontend import (
|
||||||
DOMAIN, CONF_THEMES, CONF_EXTRA_HTML_URL, DATA_PANELS)
|
DOMAIN, CONF_JS_VERSION, CONF_THEMES, CONF_EXTRA_HTML_URL,
|
||||||
|
CONF_EXTRA_HTML_URL_ES5, DATA_PANELS)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -36,7 +37,10 @@ def mock_http_client_with_urls(hass, test_client):
|
|||||||
"""Start the Hass HTTP component."""
|
"""Start the Hass HTTP component."""
|
||||||
hass.loop.run_until_complete(async_setup_component(hass, 'frontend', {
|
hass.loop.run_until_complete(async_setup_component(hass, 'frontend', {
|
||||||
DOMAIN: {
|
DOMAIN: {
|
||||||
CONF_EXTRA_HTML_URL: ["https://domain.com/my_extra_url.html"]
|
CONF_JS_VERSION: 'auto',
|
||||||
|
CONF_EXTRA_HTML_URL: ["https://domain.com/my_extra_url.html"],
|
||||||
|
CONF_EXTRA_HTML_URL_ES5:
|
||||||
|
["https://domain.com/my_extra_url_es5.html"]
|
||||||
}}))
|
}}))
|
||||||
return hass.loop.run_until_complete(test_client(hass.http.app))
|
return hass.loop.run_until_complete(test_client(hass.http.app))
|
||||||
|
|
||||||
@ -163,12 +167,21 @@ def test_missing_themes(mock_http_client):
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_extra_urls(mock_http_client_with_urls):
|
def test_extra_urls(mock_http_client_with_urls):
|
||||||
"""Test that extra urls are loaded."""
|
"""Test that extra urls are loaded."""
|
||||||
resp = yield from mock_http_client_with_urls.get('/states')
|
resp = yield from mock_http_client_with_urls.get('/states?latest')
|
||||||
assert resp.status == 200
|
assert resp.status == 200
|
||||||
text = yield from resp.text()
|
text = yield from resp.text()
|
||||||
assert text.find('href="https://domain.com/my_extra_url.html"') >= 0
|
assert text.find('href="https://domain.com/my_extra_url.html"') >= 0
|
||||||
|
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def test_extra_urls_es5(mock_http_client_with_urls):
|
||||||
|
"""Test that es5 extra urls are loaded."""
|
||||||
|
resp = yield from mock_http_client_with_urls.get('/states?es5')
|
||||||
|
assert resp.status == 200
|
||||||
|
text = yield from resp.text()
|
||||||
|
assert text.find('href="https://domain.com/my_extra_url_es5.html"') >= 0
|
||||||
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def test_panel_without_path(hass):
|
def test_panel_without_path(hass):
|
||||||
"""Test panel registration without file path."""
|
"""Test panel registration without file path."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user