mirror of
https://github.com/home-assistant/core.git
synced 2025-07-22 20:57:21 +00:00
Further cleanup frontend (#14805)
* Remove registering panels * Remove unused image * Lint
This commit is contained in:
parent
ad9621ebe5
commit
b3b4f7468d
@ -5,7 +5,6 @@ For more details about this component, please refer to the documentation at
|
||||
https://home-assistant.io/components/frontend/
|
||||
"""
|
||||
import asyncio
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
@ -30,8 +29,6 @@ REQUIREMENTS = ['home-assistant-frontend==20180603.0']
|
||||
DOMAIN = 'frontend'
|
||||
DEPENDENCIES = ['api', 'websocket_api', 'http', 'system_log']
|
||||
|
||||
URL_PANEL_COMPONENT_FP = '/frontend/panels/{}-{}.html'
|
||||
|
||||
CONF_THEMES = 'themes'
|
||||
CONF_EXTRA_HTML_URL = 'extra_html_url'
|
||||
CONF_EXTRA_HTML_URL_ES5 = 'extra_html_url_es5'
|
||||
@ -101,7 +98,7 @@ SCHEMA_GET_PANELS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
|
||||
})
|
||||
|
||||
|
||||
class AbstractPanel:
|
||||
class Panel:
|
||||
"""Abstract class for panels."""
|
||||
|
||||
# Name of the webcomponent
|
||||
@ -113,44 +110,12 @@ class AbstractPanel:
|
||||
# Title to show in the sidebar (optional)
|
||||
sidebar_title = None
|
||||
|
||||
# Url to the webcomponent (depending on JS version)
|
||||
webcomponent_url_es5 = None
|
||||
webcomponent_url_latest = None
|
||||
|
||||
# Url to show the panel in the frontend
|
||||
frontend_url_path = None
|
||||
|
||||
# Config to pass to the webcomponent
|
||||
config = None
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_register(self, hass):
|
||||
"""Register panel with HASS."""
|
||||
panels = hass.data.get(DATA_PANELS)
|
||||
if panels is None:
|
||||
panels = hass.data[DATA_PANELS] = {}
|
||||
|
||||
if self.frontend_url_path in panels:
|
||||
_LOGGER.warning("Overwriting component %s", self.frontend_url_path)
|
||||
|
||||
if DATA_FINALIZE_PANEL in hass.data:
|
||||
yield from hass.data[DATA_FINALIZE_PANEL](self)
|
||||
|
||||
panels[self.frontend_url_path] = self
|
||||
|
||||
@callback
|
||||
def async_register_index_routes(self, router, index_view):
|
||||
"""Register routes for panel to be served by index view."""
|
||||
router.add_route(
|
||||
'get', '/{}'.format(self.frontend_url_path), index_view.get)
|
||||
router.add_route(
|
||||
'get', '/{}/{{extra:.+}}'.format(self.frontend_url_path),
|
||||
index_view.get)
|
||||
|
||||
|
||||
class BuiltInPanel(AbstractPanel):
|
||||
"""Panel that is part of hass_frontend."""
|
||||
|
||||
def __init__(self, component_name, sidebar_title, sidebar_icon,
|
||||
frontend_url_path, config):
|
||||
"""Initialize a built-in panel."""
|
||||
@ -160,6 +125,16 @@ class BuiltInPanel(AbstractPanel):
|
||||
self.frontend_url_path = frontend_url_path or component_name
|
||||
self.config = config
|
||||
|
||||
@callback
|
||||
def async_register_index_routes(self, router, index_view):
|
||||
"""Register routes for panel to be served by index view."""
|
||||
router.add_route(
|
||||
'get', '/{}'.format(self.frontend_url_path), index_view.get)
|
||||
router.add_route(
|
||||
'get', '/{}/{{extra:.+}}'.format(self.frontend_url_path),
|
||||
index_view.get)
|
||||
|
||||
@callback
|
||||
def to_response(self, hass, request):
|
||||
"""Panel as dictionary."""
|
||||
return {
|
||||
@ -171,95 +146,25 @@ class BuiltInPanel(AbstractPanel):
|
||||
}
|
||||
|
||||
|
||||
class ExternalPanel(AbstractPanel):
|
||||
"""Panel that is added by a custom component."""
|
||||
|
||||
REGISTERED_COMPONENTS = set()
|
||||
|
||||
def __init__(self, component_name, path, md5, sidebar_title, sidebar_icon,
|
||||
frontend_url_path, config):
|
||||
"""Initialize an external panel."""
|
||||
self.component_name = component_name
|
||||
self.path = path
|
||||
self.md5 = md5
|
||||
self.sidebar_title = sidebar_title
|
||||
self.sidebar_icon = sidebar_icon
|
||||
self.frontend_url_path = frontend_url_path or component_name
|
||||
self.config = config
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_finalize(self, hass, frontend_repository_path):
|
||||
"""Finalize this panel for usage.
|
||||
|
||||
frontend_repository_path is set, will be prepended to path of built-in
|
||||
components.
|
||||
"""
|
||||
try:
|
||||
if self.md5 is None:
|
||||
self.md5 = yield from hass.async_add_job(
|
||||
_fingerprint, self.path)
|
||||
except OSError:
|
||||
_LOGGER.error('Cannot find or access %s at %s',
|
||||
self.component_name, self.path)
|
||||
hass.data[DATA_PANELS].pop(self.frontend_url_path)
|
||||
return
|
||||
|
||||
self.webcomponent_url_es5 = self.webcomponent_url_latest = \
|
||||
URL_PANEL_COMPONENT_FP.format(self.component_name, self.md5)
|
||||
|
||||
if self.component_name not in self.REGISTERED_COMPONENTS:
|
||||
hass.http.register_static_path(
|
||||
self.webcomponent_url_latest, self.path,
|
||||
# if path is None, we're in prod mode, so cache static assets
|
||||
frontend_repository_path is None)
|
||||
self.REGISTERED_COMPONENTS.add(self.component_name)
|
||||
|
||||
def to_response(self, hass, request):
|
||||
"""Panel as dictionary."""
|
||||
result = {
|
||||
'component_name': self.component_name,
|
||||
'icon': self.sidebar_icon,
|
||||
'title': self.sidebar_title,
|
||||
'url_path': self.frontend_url_path,
|
||||
'config': self.config,
|
||||
}
|
||||
if _is_latest(hass.data[DATA_JS_VERSION], request):
|
||||
result['url'] = self.webcomponent_url_latest
|
||||
else:
|
||||
result['url'] = self.webcomponent_url_es5
|
||||
return result
|
||||
|
||||
|
||||
@bind_hass
|
||||
@asyncio.coroutine
|
||||
def async_register_built_in_panel(hass, component_name, sidebar_title=None,
|
||||
sidebar_icon=None, frontend_url_path=None,
|
||||
config=None):
|
||||
async def async_register_built_in_panel(hass, component_name,
|
||||
sidebar_title=None, sidebar_icon=None,
|
||||
frontend_url_path=None, config=None):
|
||||
"""Register a built-in panel."""
|
||||
panel = BuiltInPanel(component_name, sidebar_title, sidebar_icon,
|
||||
frontend_url_path, config)
|
||||
yield from panel.async_register(hass)
|
||||
panel = Panel(component_name, sidebar_title, sidebar_icon,
|
||||
frontend_url_path, config)
|
||||
|
||||
panels = hass.data.get(DATA_PANELS)
|
||||
if panels is None:
|
||||
panels = hass.data[DATA_PANELS] = {}
|
||||
|
||||
@bind_hass
|
||||
@asyncio.coroutine
|
||||
def async_register_panel(hass, component_name, path, md5=None,
|
||||
sidebar_title=None, sidebar_icon=None,
|
||||
frontend_url_path=None, config=None):
|
||||
"""Register a panel for the frontend.
|
||||
if panel.frontend_url_path in panels:
|
||||
_LOGGER.warning("Overwriting component %s", panel.frontend_url_path)
|
||||
|
||||
component_name: name of the web component
|
||||
path: path to the HTML of the web component
|
||||
(required unless url is provided)
|
||||
md5: the md5 hash of the web component (for versioning in URL, optional)
|
||||
sidebar_title: title to show in the sidebar (optional)
|
||||
sidebar_icon: icon to show next to title in sidebar (optional)
|
||||
url_path: name to use in the URL (defaults to component_name)
|
||||
config: config to be passed into the web component
|
||||
"""
|
||||
panel = ExternalPanel(component_name, path, md5, sidebar_title,
|
||||
sidebar_icon, frontend_url_path, config)
|
||||
yield from panel.async_register(hass)
|
||||
if DATA_FINALIZE_PANEL in hass.data:
|
||||
hass.data[DATA_FINALIZE_PANEL](panel)
|
||||
|
||||
panels[panel.frontend_url_path] = panel
|
||||
|
||||
|
||||
@bind_hass
|
||||
@ -278,11 +183,10 @@ def add_manifest_json_key(key, val):
|
||||
MANIFEST_JSON[key] = val
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_setup(hass, config):
|
||||
async def async_setup(hass, config):
|
||||
"""Set up the serving of the frontend."""
|
||||
if list(hass.auth.async_auth_providers):
|
||||
client = yield from hass.auth.async_create_client(
|
||||
client = await hass.auth.async_create_client(
|
||||
'Home Assistant Frontend',
|
||||
redirect_uris=['/'],
|
||||
no_secret=True,
|
||||
@ -331,24 +235,22 @@ def async_setup(hass, config):
|
||||
index_view = IndexView(repo_path, js_version, client)
|
||||
hass.http.register_view(index_view)
|
||||
|
||||
async def finalize_panel(panel):
|
||||
@callback
|
||||
def async_finalize_panel(panel):
|
||||
"""Finalize setup of a panel."""
|
||||
if hasattr(panel, 'async_finalize'):
|
||||
await panel.async_finalize(hass, repo_path)
|
||||
panel.async_register_index_routes(hass.http.app.router, index_view)
|
||||
|
||||
yield from asyncio.wait([
|
||||
await asyncio.wait([
|
||||
async_register_built_in_panel(hass, panel)
|
||||
for panel in ('dev-event', 'dev-info', 'dev-service', 'dev-state',
|
||||
'dev-template', 'dev-mqtt', 'kiosk')], loop=hass.loop)
|
||||
|
||||
hass.data[DATA_FINALIZE_PANEL] = finalize_panel
|
||||
hass.data[DATA_FINALIZE_PANEL] = async_finalize_panel
|
||||
|
||||
# Finalize registration of panels that registered before frontend was setup
|
||||
# This includes the built-in panels from line above.
|
||||
yield from asyncio.wait(
|
||||
[finalize_panel(panel) for panel in hass.data[DATA_PANELS].values()],
|
||||
loop=hass.loop)
|
||||
for panel in hass.data[DATA_PANELS].values():
|
||||
async_finalize_panel(panel)
|
||||
|
||||
if DATA_EXTRA_HTML_URL not in hass.data:
|
||||
hass.data[DATA_EXTRA_HTML_URL] = set()
|
||||
@ -456,38 +358,23 @@ class IndexView(HomeAssistantView):
|
||||
|
||||
return tpl
|
||||
|
||||
@asyncio.coroutine
|
||||
def get(self, request, extra=None):
|
||||
async def get(self, request, extra=None):
|
||||
"""Serve the index view."""
|
||||
hass = request.app['hass']
|
||||
latest = self.repo_path is not None or \
|
||||
_is_latest(self.js_option, request)
|
||||
|
||||
if request.path == '/':
|
||||
panel = 'states'
|
||||
else:
|
||||
panel = request.path.split('/')[1]
|
||||
|
||||
if panel == 'states':
|
||||
panel_url = ''
|
||||
elif latest:
|
||||
panel_url = hass.data[DATA_PANELS][panel].webcomponent_url_latest
|
||||
else:
|
||||
panel_url = hass.data[DATA_PANELS][panel].webcomponent_url_es5
|
||||
|
||||
no_auth = '1'
|
||||
if hass.config.api.api_password and not request[KEY_AUTHENTICATED]:
|
||||
# do not try to auto connect on load
|
||||
no_auth = '0'
|
||||
|
||||
template = yield from hass.async_add_job(self.get_template, latest)
|
||||
template = await hass.async_add_job(self.get_template, latest)
|
||||
|
||||
extra_key = DATA_EXTRA_HTML_URL if latest else DATA_EXTRA_HTML_URL_ES5
|
||||
|
||||
template_params = dict(
|
||||
no_auth=no_auth,
|
||||
panel_url=panel_url,
|
||||
panels=hass.data[DATA_PANELS],
|
||||
theme_color=MANIFEST_JSON['theme_color'],
|
||||
extra_urls=hass.data[extra_key],
|
||||
)
|
||||
@ -506,7 +393,7 @@ class ManifestJSONView(HomeAssistantView):
|
||||
url = '/manifest.json'
|
||||
name = 'manifestjson'
|
||||
|
||||
@asyncio.coroutine
|
||||
@callback
|
||||
def get(self, request): # pylint: disable=no-self-use
|
||||
"""Return the manifest.json."""
|
||||
msg = json.dumps(MANIFEST_JSON, sort_keys=True)
|
||||
@ -537,23 +424,16 @@ class TranslationsView(HomeAssistantView):
|
||||
url = '/api/translations/{language}'
|
||||
name = 'api:translations'
|
||||
|
||||
@asyncio.coroutine
|
||||
def get(self, request, language):
|
||||
async def get(self, request, language):
|
||||
"""Return translations."""
|
||||
hass = request.app['hass']
|
||||
|
||||
resources = yield from async_get_translations(hass, language)
|
||||
resources = await async_get_translations(hass, language)
|
||||
return self.json({
|
||||
'resources': resources,
|
||||
})
|
||||
|
||||
|
||||
def _fingerprint(path):
|
||||
"""Fingerprint a file."""
|
||||
with open(path) as fil:
|
||||
return hashlib.md5(fil.read().encode('utf-8')).hexdigest()
|
||||
|
||||
|
||||
def _is_latest(js_option, request):
|
||||
"""
|
||||
Return whether we should serve latest untranspiled code.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 7.6 KiB |
@ -8,7 +8,7 @@ import pytest
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.components.frontend import (
|
||||
DOMAIN, CONF_JS_VERSION, CONF_THEMES, CONF_EXTRA_HTML_URL,
|
||||
CONF_EXTRA_HTML_URL_ES5, DATA_PANELS)
|
||||
CONF_EXTRA_HTML_URL_ES5)
|
||||
from homeassistant.components import websocket_api as wapi
|
||||
|
||||
|
||||
@ -183,15 +183,6 @@ def test_extra_urls_es5(mock_http_client_with_urls):
|
||||
assert text.find('href="https://domain.com/my_extra_url_es5.html"') >= 0
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def test_panel_without_path(hass):
|
||||
"""Test panel registration without file path."""
|
||||
yield from hass.components.frontend.async_register_panel(
|
||||
'test_component', 'nonexistant_file')
|
||||
yield from async_setup_component(hass, 'frontend', {})
|
||||
assert 'test_component' not in hass.data[DATA_PANELS]
|
||||
|
||||
|
||||
async def test_get_panels(hass, hass_ws_client):
|
||||
"""Test get_panels command."""
|
||||
await async_setup_component(hass, 'frontend')
|
||||
|
Loading…
x
Reference in New Issue
Block a user