From bd54ff3c02cdc38cad647e9e658b907897f39f0e Mon Sep 17 00:00:00 2001 From: Paulus Schoutsen Date: Fri, 8 Nov 2019 01:06:16 -0800 Subject: [PATCH] Add TT WS API (#28599) * Add TT WS API * Add a test * Correctly convert TT errrors --- homeassistant/components/cloud/http_api.py | 46 ++++++++++------ homeassistant/components/cloud/manifest.json | 2 +- .../components/websocket_api/connection.py | 6 ++- homeassistant/package_constraints.txt | 2 +- requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- tests/components/cloud/test_http_api.py | 53 +++++++++++++++++++ 7 files changed, 93 insertions(+), 20 deletions(-) diff --git a/homeassistant/components/cloud/http_api.py b/homeassistant/components/cloud/http_api.py index 97c96b0a3e8..d969612ce8e 100644 --- a/homeassistant/components/cloud/http_api.py +++ b/homeassistant/components/cloud/http_api.py @@ -6,7 +6,7 @@ import logging import aiohttp import async_timeout import attr -from hass_nabucasa import Cloud, auth +from hass_nabucasa import Cloud, auth, thingtalk from hass_nabucasa.const import STATE_DISCONNECTED import voluptuous as vol @@ -75,28 +75,29 @@ _CLOUD_ERRORS = { async def async_setup(hass): """Initialize the HTTP API.""" - hass.components.websocket_api.async_register_command( - WS_TYPE_STATUS, websocket_cloud_status, SCHEMA_WS_STATUS - ) - hass.components.websocket_api.async_register_command( + async_register_command = hass.components.websocket_api.async_register_command + async_register_command(WS_TYPE_STATUS, websocket_cloud_status, SCHEMA_WS_STATUS) + async_register_command( WS_TYPE_SUBSCRIPTION, websocket_subscription, SCHEMA_WS_SUBSCRIPTION ) - hass.components.websocket_api.async_register_command(websocket_update_prefs) - hass.components.websocket_api.async_register_command( + async_register_command(websocket_update_prefs) + async_register_command( WS_TYPE_HOOK_CREATE, websocket_hook_create, SCHEMA_WS_HOOK_CREATE ) - hass.components.websocket_api.async_register_command( + async_register_command( WS_TYPE_HOOK_DELETE, websocket_hook_delete, SCHEMA_WS_HOOK_DELETE ) - hass.components.websocket_api.async_register_command(websocket_remote_connect) - hass.components.websocket_api.async_register_command(websocket_remote_disconnect) + async_register_command(websocket_remote_connect) + async_register_command(websocket_remote_disconnect) - hass.components.websocket_api.async_register_command(google_assistant_list) - hass.components.websocket_api.async_register_command(google_assistant_update) + async_register_command(google_assistant_list) + async_register_command(google_assistant_update) - hass.components.websocket_api.async_register_command(alexa_list) - hass.components.websocket_api.async_register_command(alexa_update) - hass.components.websocket_api.async_register_command(alexa_sync) + async_register_command(alexa_list) + async_register_command(alexa_update) + async_register_command(alexa_sync) + + async_register_command(thingtalk_convert) hass.http.register_view(GoogleActionsSyncView) hass.http.register_view(CloudLoginView) @@ -592,3 +593,18 @@ async def alexa_sync(hass, connection, msg): connection.send_result(msg["id"]) else: connection.send_error(msg["id"], ws_const.ERR_UNKNOWN_ERROR, "Unknown error") + + +@websocket_api.async_response +@websocket_api.websocket_command({"type": "cloud/thingtalk/convert", "query": str}) +async def thingtalk_convert(hass, connection, msg): + """Convert a query.""" + cloud = hass.data[DOMAIN] + + with async_timeout.timeout(10): + try: + connection.send_result( + msg["id"], await thingtalk.async_convert(cloud, msg["query"]) + ) + except thingtalk.ThingTalkConversionError as err: + connection.send_error(msg["id"], ws_const.ERR_UNKNOWN_ERROR, str(err)) diff --git a/homeassistant/components/cloud/manifest.json b/homeassistant/components/cloud/manifest.json index 2876ff11b7e..2feef55835e 100644 --- a/homeassistant/components/cloud/manifest.json +++ b/homeassistant/components/cloud/manifest.json @@ -2,7 +2,7 @@ "domain": "cloud", "name": "Cloud", "documentation": "https://www.home-assistant.io/integrations/cloud", - "requirements": ["hass-nabucasa==0.26"], + "requirements": ["hass-nabucasa==0.29"], "dependencies": ["http", "webhook"], "codeowners": ["@home-assistant/cloud"] } diff --git a/homeassistant/components/websocket_api/connection.py b/homeassistant/components/websocket_api/connection.py index 41232b097d1..5a0284a34d4 100644 --- a/homeassistant/components/websocket_api/connection.py +++ b/homeassistant/components/websocket_api/connection.py @@ -108,6 +108,8 @@ class ActiveConnection: @callback def async_handle_exception(self, msg, err): """Handle an exception while processing a handler.""" + log_handler = self.logger.error + if isinstance(err, Unauthorized): code = const.ERR_UNAUTHORIZED err_message = "Unauthorized" @@ -120,6 +122,8 @@ class ActiveConnection: else: code = const.ERR_UNKNOWN_ERROR err_message = "Unknown error" + log_handler = self.logger.exception + + log_handler("Error handling message: %s", err_message) - self.logger.exception("Error handling message: %s", err_message) self.send_message(messages.error_message(msg["id"], code, err_message)) diff --git a/homeassistant/package_constraints.txt b/homeassistant/package_constraints.txt index 775545c4ff1..90022bb60a0 100644 --- a/homeassistant/package_constraints.txt +++ b/homeassistant/package_constraints.txt @@ -10,7 +10,7 @@ certifi>=2019.9.11 contextvars==2.4;python_version<"3.7" cryptography==2.8 distro==1.4.0 -hass-nabucasa==0.26 +hass-nabucasa==0.29 home-assistant-frontend==20191025.1 importlib-metadata==0.23 jinja2>=2.10.3 diff --git a/requirements_all.txt b/requirements_all.txt index 2efb1db7ba0..80d01e9ce72 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -625,7 +625,7 @@ habitipy==0.2.0 hangups==0.4.9 # homeassistant.components.cloud -hass-nabucasa==0.26 +hass-nabucasa==0.29 # homeassistant.components.mqtt hbmqtt==0.9.5 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index f29eee878d9..4f4203480cd 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -204,7 +204,7 @@ ha-ffmpeg==2.0 hangups==0.4.9 # homeassistant.components.cloud -hass-nabucasa==0.26 +hass-nabucasa==0.29 # homeassistant.components.mqtt hbmqtt==0.9.5 diff --git a/tests/components/cloud/test_http_api.py b/tests/components/cloud/test_http_api.py index 314db3a9e88..8d05f1a14c3 100644 --- a/tests/components/cloud/test_http_api.py +++ b/tests/components/cloud/test_http_api.py @@ -7,6 +7,7 @@ import pytest from jose import jwt from hass_nabucasa.auth import Unauthenticated, UnknownError from hass_nabucasa.const import STATE_CONNECTED +from hass_nabucasa import thingtalk from homeassistant.core import State from homeassistant.auth.providers import trusted_networks as tn_auth @@ -871,3 +872,55 @@ async def test_enable_alexa_state_report_fail( assert not response["success"] assert response["error"]["code"] == "alexa_relink" + + +async def test_thingtalk_convert(hass, hass_ws_client, setup_api): + """Test that we can convert a query.""" + client = await hass_ws_client(hass) + + with patch( + "homeassistant.components.cloud.http_api.thingtalk.async_convert", + return_value=mock_coro({"hello": "world"}), + ): + await client.send_json( + {"id": 5, "type": "cloud/thingtalk/convert", "query": "some-data"} + ) + response = await client.receive_json() + + assert response["success"] + assert response["result"] == {"hello": "world"} + + +async def test_thingtalk_convert_timeout(hass, hass_ws_client, setup_api): + """Test that we can convert a query.""" + client = await hass_ws_client(hass) + + with patch( + "homeassistant.components.cloud.http_api.thingtalk.async_convert", + side_effect=asyncio.TimeoutError, + ): + await client.send_json( + {"id": 5, "type": "cloud/thingtalk/convert", "query": "some-data"} + ) + response = await client.receive_json() + + assert not response["success"] + assert response["error"]["code"] == "timeout" + + +async def test_thingtalk_convert_internal(hass, hass_ws_client, setup_api): + """Test that we can convert a query.""" + client = await hass_ws_client(hass) + + with patch( + "homeassistant.components.cloud.http_api.thingtalk.async_convert", + side_effect=thingtalk.ThingTalkConversionError("Did not understand"), + ): + await client.send_json( + {"id": 5, "type": "cloud/thingtalk/convert", "query": "some-data"} + ) + response = await client.receive_json() + + assert not response["success"] + assert response["error"]["code"] == "unknown_error" + assert response["error"]["message"] == "Did not understand"