Add panel custom to load any webcomponent (#2747)
This commit is contained in:
parent
9a575eb6d6
commit
8081fe794e
5 changed files with 161 additions and 31 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -7,7 +7,9 @@ config/custom_components/*
|
||||||
!config/custom_components/example.py
|
!config/custom_components/example.py
|
||||||
!config/custom_components/hello_world.py
|
!config/custom_components/hello_world.py
|
||||||
!config/custom_components/mqtt_example.py
|
!config/custom_components/mqtt_example.py
|
||||||
!config/custom_components/react_panel
|
!config/panels
|
||||||
|
config/panels/*
|
||||||
|
!config/panels/react.html
|
||||||
|
|
||||||
tests/testing_config/deps
|
tests/testing_config/deps
|
||||||
tests/testing_config/home-assistant.log
|
tests/testing_config/home-assistant.log
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
"""
|
|
||||||
Custom panel example showing TodoMVC using React.
|
|
||||||
|
|
||||||
Will add a panel to control lights and switches using React. Allows configuring
|
|
||||||
the title via configuration.yaml:
|
|
||||||
|
|
||||||
react_panel:
|
|
||||||
title: 'home'
|
|
||||||
|
|
||||||
"""
|
|
||||||
import os
|
|
||||||
|
|
||||||
from homeassistant.components.frontend import register_panel
|
|
||||||
|
|
||||||
DOMAIN = 'react_panel'
|
|
||||||
DEPENDENCIES = ['frontend']
|
|
||||||
|
|
||||||
PANEL_PATH = os.path.join(os.path.dirname(__file__), 'panel.html')
|
|
||||||
|
|
||||||
|
|
||||||
def setup(hass, config):
|
|
||||||
"""Initialize custom panel."""
|
|
||||||
title = config.get(DOMAIN, {}).get('title')
|
|
||||||
|
|
||||||
config = None if title is None else {'title': title}
|
|
||||||
|
|
||||||
register_panel(hass, 'react', PANEL_PATH,
|
|
||||||
title='TodoMVC', icon='mdi:checkbox-marked-outline',
|
|
||||||
config=config)
|
|
||||||
return True
|
|
|
@ -1,3 +1,20 @@
|
||||||
|
<!--
|
||||||
|
Custom Home Assistant panel example.
|
||||||
|
|
||||||
|
Currently only works in Firefox and Chrome because it uses ES6.
|
||||||
|
|
||||||
|
Make sure this file is in <config>/panels/react.html
|
||||||
|
|
||||||
|
Add to your configuration.yaml:
|
||||||
|
|
||||||
|
panel_custom:
|
||||||
|
- name: react
|
||||||
|
sidebar_title: TodoMVC
|
||||||
|
sidebar_icon: mdi:checkbox-marked-outline
|
||||||
|
config:
|
||||||
|
title: Wow hello!
|
||||||
|
-->
|
||||||
|
|
||||||
<script src="https://fb.me/react-15.2.1.min.js"></script>
|
<script src="https://fb.me/react-15.2.1.min.js"></script>
|
||||||
<script src="https://fb.me/react-dom-15.2.1.min.js"></script>
|
<script src="https://fb.me/react-dom-15.2.1.min.js"></script>
|
||||||
|
|
64
homeassistant/components/panel_custom.py
Normal file
64
homeassistant/components/panel_custom.py
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
"""Register a custom front end panel."""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import voluptuous as vol
|
||||||
|
|
||||||
|
import homeassistant.helpers.config_validation as cv
|
||||||
|
from homeassistant.components.frontend import register_panel
|
||||||
|
|
||||||
|
DOMAIN = 'panel_custom'
|
||||||
|
DEPENDENCIES = ['frontend']
|
||||||
|
|
||||||
|
CONF_COMPONENT_NAME = 'name'
|
||||||
|
CONF_SIDEBAR_TITLE = 'sidebar_title'
|
||||||
|
CONF_SIDEBAR_ICON = 'sidebar_icon'
|
||||||
|
CONF_URL_PATH = 'url_path'
|
||||||
|
CONF_CONFIG = 'config'
|
||||||
|
CONF_WEBCOMPONENT_PATH = 'webcomponent_path'
|
||||||
|
|
||||||
|
DEFAULT_ICON = 'mdi:bookmark'
|
||||||
|
|
||||||
|
PANEL_DIR = 'panels'
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = vol.Schema({
|
||||||
|
DOMAIN: vol.All(cv.ensure_list, [{
|
||||||
|
vol.Required(CONF_COMPONENT_NAME): cv.slug,
|
||||||
|
vol.Optional(CONF_SIDEBAR_TITLE): cv.string,
|
||||||
|
vol.Optional(CONF_SIDEBAR_ICON, default=DEFAULT_ICON): cv.icon,
|
||||||
|
vol.Optional(CONF_URL_PATH): cv.string,
|
||||||
|
vol.Optional(CONF_CONFIG): cv.match_all,
|
||||||
|
vol.Optional(CONF_WEBCOMPONENT_PATH): cv.isfile,
|
||||||
|
}])
|
||||||
|
}, extra=vol.ALLOW_EXTRA)
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def setup(hass, config):
|
||||||
|
"""Initialize custom panel."""
|
||||||
|
success = False
|
||||||
|
|
||||||
|
for panel in config.get(DOMAIN):
|
||||||
|
name = panel.get(CONF_COMPONENT_NAME)
|
||||||
|
panel_path = panel.get(CONF_WEBCOMPONENT_PATH)
|
||||||
|
|
||||||
|
if panel_path is None:
|
||||||
|
panel_path = hass.config.path(PANEL_DIR, '{}.html'.format(name))
|
||||||
|
|
||||||
|
if not os.path.isfile(panel_path):
|
||||||
|
_LOGGER.error('Unable to find webcomponent for %s: %s',
|
||||||
|
name, panel_path)
|
||||||
|
continue
|
||||||
|
|
||||||
|
register_panel(
|
||||||
|
hass, name, panel_path,
|
||||||
|
sidebar_title=panel.get(CONF_SIDEBAR_TITLE),
|
||||||
|
sidebar_icon=panel.get(CONF_SIDEBAR_ICON),
|
||||||
|
url_path=panel.get(CONF_URL_PATH),
|
||||||
|
config=panel.get(CONF_CONFIG),
|
||||||
|
)
|
||||||
|
|
||||||
|
success = True
|
||||||
|
|
||||||
|
return success
|
77
tests/components/test_panel_custom.py
Normal file
77
tests/components/test_panel_custom.py
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
"""The tests for the panel_custom component."""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
|
import unittest
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
from homeassistant import bootstrap
|
||||||
|
from homeassistant.components import panel_custom
|
||||||
|
|
||||||
|
from tests.common import get_test_home_assistant
|
||||||
|
|
||||||
|
|
||||||
|
@patch('homeassistant.components.frontend.setup', return_value=True)
|
||||||
|
class TestPanelCustom(unittest.TestCase):
|
||||||
|
"""Test the panel_custom component."""
|
||||||
|
|
||||||
|
def setup_method(self, method):
|
||||||
|
"""Setup things to be run when tests are started."""
|
||||||
|
self.hass = get_test_home_assistant()
|
||||||
|
|
||||||
|
def teardown_method(self, method):
|
||||||
|
"""Stop everything that was started."""
|
||||||
|
self.hass.stop()
|
||||||
|
shutil.rmtree(self.hass.config.path(panel_custom.PANEL_DIR),
|
||||||
|
ignore_errors=True)
|
||||||
|
|
||||||
|
@patch('homeassistant.components.panel_custom.register_panel')
|
||||||
|
def test_webcomponent_in_panels_dir(self, mock_register, _mock_setup):
|
||||||
|
"""Test if a web component is found in config panels dir."""
|
||||||
|
config = {
|
||||||
|
'panel_custom': {
|
||||||
|
'name': 'todomvc',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert not bootstrap.setup_component(self.hass, 'panel_custom', config)
|
||||||
|
assert not mock_register.called
|
||||||
|
|
||||||
|
path = self.hass.config.path(panel_custom.PANEL_DIR)
|
||||||
|
os.mkdir(path)
|
||||||
|
|
||||||
|
with open(os.path.join(path, 'todomvc.html'), 'a'):
|
||||||
|
assert bootstrap.setup_component(self.hass, 'panel_custom', config)
|
||||||
|
assert mock_register.called
|
||||||
|
|
||||||
|
@patch('homeassistant.components.panel_custom.register_panel')
|
||||||
|
def test_webcomponent_custom_path(self, mock_register, _mock_setup):
|
||||||
|
"""Test if a web component is found in config panels dir."""
|
||||||
|
with NamedTemporaryFile() as fp:
|
||||||
|
config = {
|
||||||
|
'panel_custom': {
|
||||||
|
'name': 'todomvc',
|
||||||
|
'webcomponent_path': fp.name,
|
||||||
|
'sidebar_title': 'Sidebar Title',
|
||||||
|
'sidebar_icon': 'mdi:iconicon',
|
||||||
|
'url_path': 'nice_url',
|
||||||
|
'config': 5,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
with patch('os.path.isfile', return_value=False):
|
||||||
|
assert not bootstrap.setup_component(self.hass, 'panel_custom',
|
||||||
|
config)
|
||||||
|
assert not mock_register.called
|
||||||
|
|
||||||
|
assert bootstrap.setup_component(self.hass, 'panel_custom', config)
|
||||||
|
assert mock_register.called
|
||||||
|
args = mock_register.mock_calls[0][1]
|
||||||
|
kwargs = mock_register.mock_calls[0][2]
|
||||||
|
assert args == (self.hass, 'todomvc', fp.name)
|
||||||
|
assert kwargs == {
|
||||||
|
'config': 5,
|
||||||
|
'url_path': 'nice_url',
|
||||||
|
'sidebar_icon': 'mdi:iconicon',
|
||||||
|
'sidebar_title': 'Sidebar Title'
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue