diff --git a/homeassistant/components/frontend/__init__.py b/homeassistant/components/frontend/__init__.py
index 3f2f9ded22a8cc0d482b653cc86854b2b8b7169e..f9ace910481e1971f7748db9da5629e62b928252 100644
--- a/homeassistant/components/frontend/__init__.py
+++ b/homeassistant/components/frontend/__init__.py
@@ -96,6 +96,15 @@ WS_TYPE_GET_PANELS = 'get_panels'
 SCHEMA_GET_PANELS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
     vol.Required('type'): WS_TYPE_GET_PANELS,
 })
+WS_TYPE_GET_THEMES = 'frontend/get_themes'
+SCHEMA_GET_THEMES = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
+    vol.Required('type'): WS_TYPE_GET_THEMES,
+})
+WS_TYPE_GET_TRANSLATIONS = 'frontend/get_translations'
+SCHEMA_GET_TRANSLATIONS = websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend({
+    vol.Required('type'): WS_TYPE_GET_TRANSLATIONS,
+    vol.Required('language'): str,
+})
 
 
 class Panel:
@@ -195,7 +204,12 @@ async def async_setup(hass, config):
         client = None
 
     hass.components.websocket_api.async_register_command(
-        WS_TYPE_GET_PANELS, websocket_handle_get_panels, SCHEMA_GET_PANELS)
+        WS_TYPE_GET_PANELS, websocket_get_panels, SCHEMA_GET_PANELS)
+    hass.components.websocket_api.async_register_command(
+        WS_TYPE_GET_THEMES, websocket_get_themes, SCHEMA_GET_THEMES)
+    hass.components.websocket_api.async_register_command(
+        WS_TYPE_GET_TRANSLATIONS, websocket_get_translations,
+        SCHEMA_GET_TRANSLATIONS)
     hass.http.register_view(ManifestJSONView)
 
     conf = config.get(DOMAIN, {})
@@ -262,16 +276,14 @@ async def async_setup(hass, config):
     for url in conf.get(CONF_EXTRA_HTML_URL_ES5, []):
         add_extra_html_url(hass, url, True)
 
-    async_setup_themes(hass, conf.get(CONF_THEMES))
-
-    hass.http.register_view(TranslationsView)
+    _async_setup_themes(hass, conf.get(CONF_THEMES))
 
     return True
 
 
-def async_setup_themes(hass, themes):
+@callback
+def _async_setup_themes(hass, themes):
     """Set up themes data and services."""
-    hass.http.register_view(ThemesView)
     hass.data[DATA_DEFAULT_THEME] = DEFAULT_THEME
     if themes is None:
         hass.data[DATA_THEMES] = {}
@@ -400,40 +412,6 @@ class ManifestJSONView(HomeAssistantView):
         return web.Response(text=msg, content_type="application/manifest+json")
 
 
-class ThemesView(HomeAssistantView):
-    """View to return defined themes."""
-
-    requires_auth = False
-    url = '/api/themes'
-    name = 'api:themes'
-
-    @callback
-    def get(self, request):
-        """Return themes."""
-        hass = request.app['hass']
-
-        return self.json({
-            'themes': hass.data[DATA_THEMES],
-            'default_theme': hass.data[DATA_DEFAULT_THEME],
-        })
-
-
-class TranslationsView(HomeAssistantView):
-    """View to return backend defined translations."""
-
-    url = '/api/translations/{language}'
-    name = 'api:translations'
-
-    async def get(self, request, language):
-        """Return translations."""
-        hass = request.app['hass']
-
-        resources = await async_get_translations(hass, language)
-        return self.json({
-            'resources': resources,
-        })
-
-
 def _is_latest(js_option, request):
     """
     Return whether we should serve latest untranspiled code.
@@ -467,7 +445,7 @@ def _is_latest(js_option, request):
 
 
 @callback
-def websocket_handle_get_panels(hass, connection, msg):
+def websocket_get_panels(hass, connection, msg):
     """Handle get panels command.
 
     Async friendly.
@@ -480,3 +458,33 @@ def websocket_handle_get_panels(hass, connection, msg):
 
     connection.to_write.put_nowait(websocket_api.result_message(
         msg['id'], panels))
+
+
+@callback
+def websocket_get_themes(hass, connection, msg):
+    """Handle get themes command.
+
+    Async friendly.
+    """
+    connection.to_write.put_nowait(websocket_api.result_message(msg['id'], {
+        'themes': hass.data[DATA_THEMES],
+        'default_theme': hass.data[DATA_DEFAULT_THEME],
+    }))
+
+
+@callback
+def websocket_get_translations(hass, connection, msg):
+    """Handle get translations command.
+
+    Async friendly.
+    """
+    async def send_translations():
+        """Send a camera still."""
+        resources = await async_get_translations(hass, msg['language'])
+        connection.send_message_outside(websocket_api.result_message(
+            msg['id'], {
+                'resources': resources,
+            }
+        ))
+
+    hass.async_add_job(send_translations())
diff --git a/homeassistant/components/websocket_api.py b/homeassistant/components/websocket_api.py
index 11094acd3e2a00931ecd9b8ae1b545c0a89ce18e..e16e5524f95456e1469b428ceacbe8ffb572b5ad 100644
--- a/homeassistant/components/websocket_api.py
+++ b/homeassistant/components/websocket_api.py
@@ -38,6 +38,7 @@ MAX_PENDING_MSG = 512
 ERR_ID_REUSE = 1
 ERR_INVALID_FORMAT = 2
 ERR_NOT_FOUND = 3
+ERR_UNKNOWN_COMMAND = 4
 
 TYPE_AUTH = 'auth'
 TYPE_AUTH_INVALID = 'auth_invalid'
@@ -353,8 +354,11 @@ class ActiveConnection:
                         'Identifier values have to increase.'))
 
                 elif msg['type'] not in handlers:
-                    # Unknown command
-                    break
+                    self.log_error(
+                        'Received invalid command: {}'.format(msg['type']))
+                    self.to_write.put_nowait(error_message(
+                        cur_id, ERR_UNKNOWN_COMMAND,
+                        'Unknown command.'))
 
                 else:
                     handler, schema = handlers[msg['type']]
diff --git a/tests/components/test_frontend.py b/tests/components/test_frontend.py
index 2f83d923e2bd92b623e17e9c9167a95e142b998c..2f118f24ef0933a549da6ac8e9d580c1a4b51daf 100644
--- a/tests/components/test_frontend.py
+++ b/tests/components/test_frontend.py
@@ -11,6 +11,19 @@ from homeassistant.components.frontend import (
     CONF_EXTRA_HTML_URL_ES5)
 from homeassistant.components import websocket_api as wapi
 
+from tests.common import mock_coro
+
+
+CONFIG_THEMES = {
+    DOMAIN: {
+        CONF_THEMES: {
+            'happy': {
+                'primary-color': 'red'
+            }
+        }
+    }
+}
+
 
 @pytest.fixture
 def mock_http_client(hass, aiohttp_client):
@@ -101,68 +114,109 @@ def test_states_routes(mock_http_client):
     assert resp.status == 200
 
 
-@asyncio.coroutine
-def test_themes_api(mock_http_client_with_themes):
+async def test_themes_api(hass, hass_ws_client):
     """Test that /api/themes returns correct data."""
-    resp = yield from mock_http_client_with_themes.get('/api/themes')
-    json = yield from resp.json()
-    assert json['default_theme'] == 'default'
-    assert json['themes'] == {'happy': {'primary-color': 'red'}}
+    assert await async_setup_component(hass, 'frontend', CONFIG_THEMES)
+    client = await hass_ws_client(hass)
+
+    await client.send_json({
+        'id': 5,
+        'type': 'frontend/get_themes',
+    })
+    msg = await client.receive_json()
 
+    assert msg['result']['default_theme'] == 'default'
+    assert msg['result']['themes'] == {'happy': {'primary-color': 'red'}}
 
-@asyncio.coroutine
-def test_themes_set_theme(hass, mock_http_client_with_themes):
+
+async def test_themes_set_theme(hass, hass_ws_client):
     """Test frontend.set_theme service."""
-    yield from hass.services.async_call(DOMAIN, 'set_theme', {'name': 'happy'})
-    yield from hass.async_block_till_done()
-    resp = yield from mock_http_client_with_themes.get('/api/themes')
-    json = yield from resp.json()
-    assert json['default_theme'] == 'happy'
+    assert await async_setup_component(hass, 'frontend', CONFIG_THEMES)
+    client = await hass_ws_client(hass)
 
-    yield from hass.services.async_call(
-        DOMAIN, 'set_theme', {'name': 'default'})
-    yield from hass.async_block_till_done()
-    resp = yield from mock_http_client_with_themes.get('/api/themes')
-    json = yield from resp.json()
-    assert json['default_theme'] == 'default'
+    await hass.services.async_call(
+        DOMAIN, 'set_theme', {'name': 'happy'}, blocking=True)
 
+    await client.send_json({
+        'id': 5,
+        'type': 'frontend/get_themes',
+    })
+    msg = await client.receive_json()
 
-@asyncio.coroutine
-def test_themes_set_theme_wrong_name(hass, mock_http_client_with_themes):
+    assert msg['result']['default_theme'] == 'happy'
+
+    await hass.services.async_call(
+        DOMAIN, 'set_theme', {'name': 'default'}, blocking=True)
+
+    await client.send_json({
+        'id': 6,
+        'type': 'frontend/get_themes',
+    })
+    msg = await client.receive_json()
+
+    assert msg['result']['default_theme'] == 'default'
+
+
+async def test_themes_set_theme_wrong_name(hass, hass_ws_client):
     """Test frontend.set_theme service called with wrong name."""
-    yield from hass.services.async_call(DOMAIN, 'set_theme', {'name': 'wrong'})
-    yield from hass.async_block_till_done()
-    resp = yield from mock_http_client_with_themes.get('/api/themes')
-    json = yield from resp.json()
-    assert json['default_theme'] == 'default'
+    assert await async_setup_component(hass, 'frontend', CONFIG_THEMES)
+    client = await hass_ws_client(hass)
 
+    await hass.services.async_call(
+        DOMAIN, 'set_theme', {'name': 'wrong'}, blocking=True)
 
-@asyncio.coroutine
-def test_themes_reload_themes(hass, mock_http_client_with_themes):
+    await client.send_json({
+        'id': 5,
+        'type': 'frontend/get_themes',
+    })
+
+    msg = await client.receive_json()
+
+    assert msg['result']['default_theme'] == 'default'
+
+
+async def test_themes_reload_themes(hass, hass_ws_client):
     """Test frontend.reload_themes service."""
+    assert await async_setup_component(hass, 'frontend', CONFIG_THEMES)
+    client = await hass_ws_client(hass)
+
     with patch('homeassistant.components.frontend.load_yaml_config_file',
                return_value={DOMAIN: {
                    CONF_THEMES: {
                        'sad': {'primary-color': 'blue'}
                    }}}):
-        yield from hass.services.async_call(DOMAIN, 'set_theme',
-                                            {'name': 'happy'})
-        yield from hass.services.async_call(DOMAIN, 'reload_themes')
-        yield from hass.async_block_till_done()
-        resp = yield from mock_http_client_with_themes.get('/api/themes')
-        json = yield from resp.json()
-        assert json['themes'] == {'sad': {'primary-color': 'blue'}}
-        assert json['default_theme'] == 'default'
+        await hass.services.async_call(
+            DOMAIN, 'set_theme', {'name': 'happy'}, blocking=True)
+        await hass.services.async_call(DOMAIN, 'reload_themes', blocking=True)
 
+    await client.send_json({
+        'id': 5,
+        'type': 'frontend/get_themes',
+    })
 
-@asyncio.coroutine
-def test_missing_themes(mock_http_client):
+    msg = await client.receive_json()
+
+    assert msg['result']['themes'] == {'sad': {'primary-color': 'blue'}}
+    assert msg['result']['default_theme'] == 'default'
+
+
+async def test_missing_themes(hass, hass_ws_client):
     """Test that themes API works when themes are not defined."""
-    resp = yield from mock_http_client.get('/api/themes')
-    assert resp.status == 200
-    json = yield from resp.json()
-    assert json['default_theme'] == 'default'
-    assert json['themes'] == {}
+    await async_setup_component(hass, 'frontend')
+
+    client = await hass_ws_client(hass)
+    await client.send_json({
+        'id': 5,
+        'type': 'frontend/get_themes',
+    })
+
+    msg = await client.receive_json()
+
+    assert msg['id'] == 5
+    assert msg['type'] == wapi.TYPE_RESULT
+    assert msg['success']
+    assert msg['result']['default_theme'] == 'default'
+    assert msg['result']['themes'] == {}
 
 
 @asyncio.coroutine
@@ -204,3 +258,23 @@ async def test_get_panels(hass, hass_ws_client):
     assert msg['result']['map']['url_path'] == 'map'
     assert msg['result']['map']['icon'] == 'mdi:account-location'
     assert msg['result']['map']['title'] == 'Map'
+
+
+async def test_get_translations(hass, hass_ws_client):
+    """Test get_translations command."""
+    await async_setup_component(hass, 'frontend')
+    client = await hass_ws_client(hass)
+
+    with patch('homeassistant.components.frontend.async_get_translations',
+               side_effect=lambda hass, lang: mock_coro({'lang': lang})):
+        await client.send_json({
+            'id': 5,
+            'type': 'frontend/get_translations',
+            'language': 'nl',
+        })
+        msg = await client.receive_json()
+
+    assert msg['id'] == 5
+    assert msg['type'] == wapi.TYPE_RESULT
+    assert msg['success']
+    assert msg['result'] == {'resources': {'lang': 'nl'}}
diff --git a/tests/components/test_websocket_api.py b/tests/components/test_websocket_api.py
index cff103142b0b92e976ee32315aaad19a95512ca8..fbd8584a7d14ae74c832d72580e073a0f35d4dac 100644
--- a/tests/components/test_websocket_api.py
+++ b/tests/components/test_websocket_api.py
@@ -311,8 +311,9 @@ def test_unknown_command(websocket_client):
         'type': 'unknown_command',
     })
 
-    msg = yield from websocket_client.receive()
-    assert msg.type == WSMsgType.close
+    msg = yield from websocket_client.receive_json()
+    assert not msg['success']
+    assert msg['error']['code'] == wapi.ERR_UNKNOWN_COMMAND
 
 
 async def test_auth_with_token(hass, aiohttp_client, hass_access_token):