diff --git a/homeassistant/components/script/__init__.py b/homeassistant/components/script/__init__.py index 528e454c4e6..90aefcc7aaa 100644 --- a/homeassistant/components/script/__init__.py +++ b/homeassistant/components/script/__init__.py @@ -168,8 +168,14 @@ class ScriptEntity(ToggleEntity): ATTR_NAME: self.script.name, ATTR_ENTITY_ID: self.entity_id, }, context=context) - await self.script.async_run( - kwargs.get(ATTR_VARIABLES), context) + try: + await self.script.async_run( + kwargs.get(ATTR_VARIABLES), context) + except Exception as err: # pylint: disable=broad-except + self.script.async_log_exception( + _LOGGER, "Error executing script {}".format(self.entity_id), + err) + raise err async def async_turn_off(self, **kwargs): """Turn script off.""" diff --git a/homeassistant/components/websocket_api/commands.py b/homeassistant/components/websocket_api/commands.py index 84178beef8b..b6a4185abfd 100644 --- a/homeassistant/components/websocket_api/commands.py +++ b/homeassistant/components/websocket_api/commands.py @@ -120,17 +120,21 @@ async def handle_call_service(hass, connection, msg): msg['domain'], msg['service'], msg.get('service_data'), blocking, connection.context(msg)) connection.send_message(messages.result_message(msg['id'])) - except ServiceNotFound: - connection.send_message(messages.error_message( - msg['id'], const.ERR_NOT_FOUND, 'Service not found.')) + except ServiceNotFound as err: + if err.domain == msg['domain'] and err.service == msg['service']: + connection.send_message(messages.error_message( + msg['id'], const.ERR_NOT_FOUND, 'Service not found.')) + else: + connection.send_message(messages.error_message( + msg['id'], const.ERR_HOME_ASSISTANT_ERROR, str(err))) except HomeAssistantError as err: connection.logger.exception(err) connection.send_message(messages.error_message( - msg['id'], const.ERR_HOME_ASSISTANT_ERROR, '{}'.format(err))) + msg['id'], const.ERR_HOME_ASSISTANT_ERROR, str(err))) except Exception as err: # pylint: disable=broad-except connection.logger.exception(err) connection.send_message(messages.error_message( - msg['id'], const.ERR_UNKNOWN_ERROR, '{}'.format(err))) + msg['id'], const.ERR_UNKNOWN_ERROR, str(err))) @callback diff --git a/homeassistant/exceptions.py b/homeassistant/exceptions.py index aadee3e792b..6a44af9943b 100644 --- a/homeassistant/exceptions.py +++ b/homeassistant/exceptions.py @@ -75,3 +75,7 @@ class ServiceNotFound(HomeAssistantError): self, "Service {}.{} not found".format(domain, service)) self.domain = domain self.service = service + + def __str__(self) -> str: + """Return string representation.""" + return "Unable to find service {}/{}".format(self.domain, self.service) diff --git a/tests/components/automation/test_init.py b/tests/components/automation/test_init.py index a019f65afcf..179c5f84895 100644 --- a/tests/components/automation/test_init.py +++ b/tests/components/automation/test_init.py @@ -893,4 +893,4 @@ async def test_automation_with_error_in_script(hass, caplog): hass.bus.async_fire('test_event') await hass.async_block_till_done() - assert 'Service test.automation not found' in caplog.text + assert 'Service not found' in caplog.text diff --git a/tests/components/script/test_init.py b/tests/components/script/test_init.py index 790d5c2e844..c2ff17d9444 100644 --- a/tests/components/script/test_init.py +++ b/tests/components/script/test_init.py @@ -3,6 +3,8 @@ import unittest from unittest.mock import patch, Mock +import pytest + from homeassistant.components import script from homeassistant.components.script import DOMAIN from homeassistant.const import ( @@ -11,6 +13,7 @@ from homeassistant.const import ( from homeassistant.core import Context, callback, split_entity_id from homeassistant.loader import bind_hass from homeassistant.setup import setup_component, async_setup_component +from homeassistant.exceptions import ServiceNotFound from tests.common import get_test_home_assistant @@ -300,3 +303,22 @@ async def test_shared_context(hass): state = hass.states.get('script.test') assert state is not None assert state.context == context + + +async def test_logging_script_error(hass, caplog): + """Test logging script error.""" + assert await async_setup_component(hass, 'script', { + 'script': { + 'hello': { + 'sequence': [ + {'service': 'non.existing'} + ] + } + } + }) + with pytest.raises(ServiceNotFound) as err: + await hass.services.async_call('script', 'hello', blocking=True) + + assert err.value.domain == 'non' + assert err.value.service == 'existing' + assert 'Error executing script' in caplog.text diff --git a/tests/components/websocket_api/test_commands.py b/tests/components/websocket_api/test_commands.py index 4f3be31b22c..d50501897d7 100644 --- a/tests/components/websocket_api/test_commands.py +++ b/tests/components/websocket_api/test_commands.py @@ -67,6 +67,30 @@ async def test_call_service_not_found(hass, websocket_client): assert msg['error']['code'] == const.ERR_NOT_FOUND +async def test_call_service_child_not_found(hass, websocket_client): + """Test not reporting not found errors if it's not the called service.""" + async def serv_handler(call): + await hass.services.async_call('non', 'existing') + + hass.services.async_register('domain_test', 'test_service', serv_handler) + + await websocket_client.send_json({ + 'id': 5, + 'type': 'call_service', + 'domain': 'domain_test', + 'service': 'test_service', + 'service_data': { + 'hello': 'world' + } + }) + + msg = await websocket_client.receive_json() + assert msg['id'] == 5 + assert msg['type'] == const.TYPE_RESULT + assert not msg['success'] + assert msg['error']['code'] == const.ERR_HOME_ASSISTANT_ERROR + + async def test_call_service_error(hass, websocket_client): """Test call service command with error.""" @callback