From 44311193ef4d9b746e0b53ad27c357ea24bf1016 Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Sat, 11 Feb 2017 17:29:05 -0800 Subject: [PATCH] Add config component and hassbian example panel (#5868) * Add hassbian panel * Rename to generic config panel * Allow loading hassbian as test * Add tests * Update frontend * Lint * Lint --- homeassistant/components/config/__init__.py | 28 +++++ homeassistant/components/config/hassbian.py | 118 ++++++++++++++++++ homeassistant/components/frontend/__init__.py | 15 ++- homeassistant/components/frontend/version.py | 1 + .../www_static/home-assistant-polymer | 2 +- .../www_static/panels/ha-panel-config.html | 22 ++++ .../www_static/panels/ha-panel-config.html.gz | Bin 0 -> 3470 bytes tests/common.py | 7 +- tests/components/config/__init__.py | 1 + tests/components/config/test_hassbian.py | 70 +++++++++++ tests/components/config/test_init.py | 18 +++ 11 files changed, 273 insertions(+), 9 deletions(-) create mode 100644 homeassistant/components/config/__init__.py create mode 100644 homeassistant/components/config/hassbian.py create mode 100644 homeassistant/components/frontend/www_static/panels/ha-panel-config.html create mode 100644 homeassistant/components/frontend/www_static/panels/ha-panel-config.html.gz create mode 100644 tests/components/config/__init__.py create mode 100644 tests/components/config/test_hassbian.py create mode 100644 tests/components/config/test_init.py diff --git a/homeassistant/components/config/__init__.py b/homeassistant/components/config/__init__.py new file mode 100644 index 00000000000..dc8946776f5 --- /dev/null +++ b/homeassistant/components/config/__init__.py @@ -0,0 +1,28 @@ +"""Component to interact with Hassbian tools.""" +import asyncio + +from homeassistant.bootstrap import async_prepare_setup_platform +from homeassistant.components.frontend import register_built_in_panel + +DOMAIN = 'config' +DEPENDENCIES = ['http'] + + +@asyncio.coroutine +def async_setup(hass, config): + """Setup the hassbian component.""" + register_built_in_panel(hass, 'config', 'Configuration', 'mdi:settings') + + for panel_name in ('hassbian',): + panel = yield from async_prepare_setup_platform(hass, config, DOMAIN, + panel_name) + + if not panel: + continue + + success = yield from panel.async_setup(hass) + + if success: + hass.config.components.add('{}.{}'.format(DOMAIN, panel_name)) + + return True diff --git a/homeassistant/components/config/hassbian.py b/homeassistant/components/config/hassbian.py new file mode 100644 index 00000000000..c90583c5278 --- /dev/null +++ b/homeassistant/components/config/hassbian.py @@ -0,0 +1,118 @@ +"""Component to interact with Hassbian tools.""" +import asyncio +import json +import os + +from homeassistant.components.http import HomeAssistantView + + +_TEST_OUTPUT = """ +{ + "suites": [ + { + "openzwave": [ + { + "state": "installed" + }, + { + "description": "This is the description of the Open Z-Wave suite." + } + ] + }, + { + "openelec": [ + { + "state": "not_installed" + }, + { + "description": + "OpenElec is amazing. It allows you to control the TV." + } + ] + }, + { + "mosquitto": [ + { + "state": "installing" + }, + { + "description": + "Mosquitto is an MQTT broker." + } + ] + } + ] +} +""" + + +@asyncio.coroutine +def async_setup(hass): + """Setup the hassbian config.""" + # Test if is hassbian + test_mode = 'FORCE_HASSBIAN' in os.environ + is_hassbian = test_mode + + if not is_hassbian: + return False + + hass.http.register_view(HassbianSuitesView(test_mode)) + hass.http.register_view(HassbianSuiteInstallView(test_mode)) + + return True + + +@asyncio.coroutine +def hassbian_status(hass, test_mode=False): + """Query for the Hassbian status.""" + # fetch real output when not in test mode + if test_mode: + return json.loads(_TEST_OUTPUT) + + raise Exception('Real mode not implemented yet.') + + +class HassbianSuitesView(HomeAssistantView): + """Hassbian packages endpoint.""" + + url = '/api/config/hassbian/suites' + name = 'api:config:hassbian:suites' + + def __init__(self, test_mode): + """Initialize suites view.""" + self._test_mode = test_mode + + @asyncio.coroutine + def get(self, request): + """Request suite status.""" + inp = yield from hassbian_status(request.app['hass'], self._test_mode) + + # Flatten the structure a bit + suites = {} + + for suite in inp['suites']: + key = next(iter(suite)) + info = suites[key] = {} + + for item in suite[key]: + item_key = next(iter(item)) + info[item_key] = item[item_key] + + return self.json(suites) + + +class HassbianSuiteInstallView(HomeAssistantView): + """Hassbian packages endpoint.""" + + url = '/api/config/hassbian/suites/{suite}/install' + name = 'api:config:hassbian:suite' + + def __init__(self, test_mode): + """Initialize suite view.""" + self._test_mode = test_mode + + @asyncio.coroutine + def post(self, request, suite): + """Request suite status.""" + # do real install if not in test mode + return self.json({"status": "ok"}) diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py index 4d9fb8624d8..1b4602d35b4 100644 --- a/homeassistant/components/frontend/__init__.py +++ b/homeassistant/components/frontend/__init__.py @@ -19,7 +19,7 @@ DOMAIN = 'frontend' DEPENDENCIES = ['api', 'websocket_api'] URL_PANEL_COMPONENT = '/frontend/panels/{}.html' URL_PANEL_COMPONENT_FP = '/frontend/panels/{}-{}.html' -STATIC_PATH = os.path.join(os.path.dirname(__file__), 'www_static') +STATIC_PATH = os.path.join(os.path.dirname(__file__), 'www_static/') MANIFEST_JSON = { "background_color": "#FFFFFF", "description": "Open-source home automation platform running on Python 3.", @@ -51,17 +51,22 @@ _LOGGER = logging.getLogger(__name__) def register_built_in_panel(hass, component_name, sidebar_title=None, sidebar_icon=None, url_path=None, config=None): """Register a built-in panel.""" - path = 'panels/ha-panel-{}.html'.format(component_name) + nondev_path = 'panels/ha-panel-{}.html'.format(component_name) if hass.http.development: url = ('/static/home-assistant-polymer/panels/' '{0}/ha-panel-{0}.html'.format(component_name)) + path = os.path.join( + STATIC_PATH, 'home-assistant-polymer/panels/', + '{0}/ha-panel-{0}.html'.format(component_name)) else: url = None # use default url generate mechanism + path = os.path.join(STATIC_PATH, nondev_path) - register_panel(hass, component_name, os.path.join(STATIC_PATH, path), - FINGERPRINTS[path], sidebar_title, sidebar_icon, url_path, - url, config) + # Fingerprint doesn't exist when adding new built-in panel + register_panel(hass, component_name, path, + FINGERPRINTS.get(nondev_path, 'dev'), sidebar_title, + sidebar_icon, url_path, url, config) def register_panel(hass, component_name, path, md5=None, sidebar_title=None, diff --git a/homeassistant/components/frontend/version.py b/homeassistant/components/frontend/version.py index a186e440f50..1832564b98f 100644 --- a/homeassistant/components/frontend/version.py +++ b/homeassistant/components/frontend/version.py @@ -5,6 +5,7 @@ FINGERPRINTS = { "frontend.html": "cfd75c944ab14912cfbb4fdd9027579c", "mdi.html": "c1dde43ccf5667f687c418fc8daf9668", "micromarkdown-js.html": "93b5ec4016f0bba585521cf4d18dec1a", + "panels/ha-panel-config.html": "fb50a25da4b4cfbb0d9a74bb22a31db9", "panels/ha-panel-dev-event.html": "5c82300b3cf543a92cf4297506e450e7", "panels/ha-panel-dev-info.html": "0469024d94d6270a8680df2be44ba916", "panels/ha-panel-dev-service.html": "9f749635e518a4ca7991975bdefdb10a", diff --git a/homeassistant/components/frontend/www_static/home-assistant-polymer b/homeassistant/components/frontend/www_static/home-assistant-polymer index 518d0b38da4..ba48e40a2bb 160000 --- a/homeassistant/components/frontend/www_static/home-assistant-polymer +++ b/homeassistant/components/frontend/www_static/home-assistant-polymer @@ -1 +1 @@ -Subproject commit 518d0b38da47fa5923c84a963d11e9ed59424c2f +Subproject commit ba48e40a2bba273b1cdcdf637ecbef4f4db14284 diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-config.html b/homeassistant/components/frontend/www_static/panels/ha-panel-config.html new file mode 100644 index 00000000000..aa49194bfbc --- /dev/null +++ b/homeassistant/components/frontend/www_static/panels/ha-panel-config.html @@ -0,0 +1,22 @@ + \ No newline at end of file diff --git a/homeassistant/components/frontend/www_static/panels/ha-panel-config.html.gz b/homeassistant/components/frontend/www_static/panels/ha-panel-config.html.gz new file mode 100644 index 0000000000000000000000000000000000000000..0680d628cc006a71aa2214448149a94c7f0013d5 GIT binary patch literal 3470 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