diff --git a/homeassistant/components/shell_command/__init__.py b/homeassistant/components/shell_command/__init__.py index 8430d7284ee..b2f38f54b20 100644 --- a/homeassistant/components/shell_command/__init__.py +++ b/homeassistant/components/shell_command/__init__.py @@ -105,14 +105,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: raise - service_response: JsonObjectType = { - "stdout": "", - "stderr": "", - "returncode": process.returncode, - } - if stdout_data: - service_response["stdout"] = stdout_data.decode("utf-8").strip() _LOGGER.debug( "Stdout of command: `%s`, return code: %s:\n%s", cmd, @@ -120,7 +113,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: stdout_data, ) if stderr_data: - service_response["stderr"] = stderr_data.decode("utf-8").strip() _LOGGER.debug( "Stderr of command: `%s`, return code: %s:\n%s", cmd, @@ -132,7 +124,24 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: "Error running command: `%s`, return code: %s", cmd, process.returncode ) - return service_response + if service.return_response: + service_response: JsonObjectType = { + "stdout": "", + "stderr": "", + "returncode": process.returncode, + } + try: + if stdout_data: + service_response["stdout"] = stdout_data.decode("utf-8").strip() + if stderr_data: + service_response["stderr"] = stderr_data.decode("utf-8").strip() + return service_response + except UnicodeDecodeError: + _LOGGER.exception( + "Unable to handle non-utf8 output of command: `%s`", cmd + ) + raise + return None for name in conf: hass.services.async_register( diff --git a/tests/components/shell_command/test_init.py b/tests/components/shell_command/test_init.py index ac594c811ed..1efcc9dc919 100644 --- a/tests/components/shell_command/test_init.py +++ b/tests/components/shell_command/test_init.py @@ -174,6 +174,40 @@ async def test_stdout_captured(mock_output, hass: HomeAssistant) -> None: assert response["returncode"] == 0 +@patch("homeassistant.components.shell_command._LOGGER.debug") +async def test_non_text_stdout_capture( + mock_output, hass: HomeAssistant, caplog: pytest.LogCaptureFixture +) -> None: + """Test handling of non-text output.""" + assert await async_setup_component( + hass, + shell_command.DOMAIN, + { + shell_command.DOMAIN: { + "output_image": "curl -o - https://raw.githubusercontent.com/home-assistant/assets/master/misc/loading-screen.gif" + } + }, + ) + + # No problem without 'return_response' + response = await hass.services.async_call( + "shell_command", "output_image", blocking=True + ) + + await hass.async_block_till_done() + assert not response + + # Non-text output throws with 'return_response' + with pytest.raises(UnicodeDecodeError): + response = await hass.services.async_call( + "shell_command", "output_image", blocking=True, return_response=True + ) + + await hass.async_block_till_done() + assert not response + assert "Unable to handle non-utf8 output of command" in caplog.text + + @patch("homeassistant.components.shell_command._LOGGER.debug") async def test_stderr_captured(mock_output, hass: HomeAssistant) -> None: """Test subprocess that has stderr."""