mirror of
https://github.com/home-assistant/core.git
synced 2025-07-14 16:57:10 +00:00
Handle grpc errors in Google Assistant SDK (#146438)
This commit is contained in:
parent
7da1671b06
commit
b9e8cfb291
@ -12,6 +12,7 @@ import aiohttp
|
|||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
from gassist_text import TextAssistant
|
from gassist_text import TextAssistant
|
||||||
from google.oauth2.credentials import Credentials
|
from google.oauth2.credentials import Credentials
|
||||||
|
from grpc import RpcError
|
||||||
|
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
@ -25,6 +26,7 @@ from homeassistant.components.media_player import (
|
|||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
from homeassistant.const import ATTR_ENTITY_ID, CONF_ACCESS_TOKEN
|
from homeassistant.const import ATTR_ENTITY_ID, CONF_ACCESS_TOKEN
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
|
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
|
||||||
from homeassistant.helpers.event import async_call_later
|
from homeassistant.helpers.event import async_call_later
|
||||||
|
|
||||||
@ -83,7 +85,17 @@ async def async_send_text_commands(
|
|||||||
) as assistant:
|
) as assistant:
|
||||||
command_response_list = []
|
command_response_list = []
|
||||||
for command in commands:
|
for command in commands:
|
||||||
|
try:
|
||||||
resp = await hass.async_add_executor_job(assistant.assist, command)
|
resp = await hass.async_add_executor_job(assistant.assist, command)
|
||||||
|
except RpcError as err:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Failed to send command '%s' to Google Assistant: %s",
|
||||||
|
command,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
raise HomeAssistantError(
|
||||||
|
translation_domain=DOMAIN, translation_key="grpc_error"
|
||||||
|
) from err
|
||||||
text_response = resp[0]
|
text_response = resp[0]
|
||||||
_LOGGER.debug("command: %s\nresponse: %s", command, text_response)
|
_LOGGER.debug("command: %s\nresponse: %s", command, text_response)
|
||||||
audio_response = resp[2]
|
audio_response = resp[2]
|
||||||
|
@ -57,5 +57,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"exceptions": {
|
||||||
|
"grpc_error": {
|
||||||
|
"message": "Failed to communicate with Google Assistant"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import time
|
|||||||
from unittest.mock import call, patch
|
from unittest.mock import call, patch
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
from grpc import RpcError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import conversation
|
from homeassistant.components import conversation
|
||||||
@ -13,6 +14,7 @@ from homeassistant.components.google_assistant_sdk import DOMAIN
|
|||||||
from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES
|
from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES
|
||||||
from homeassistant.config_entries import ConfigEntryState
|
from homeassistant.config_entries import ConfigEntryState
|
||||||
from homeassistant.core import Context, HomeAssistant
|
from homeassistant.core import Context, HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
from homeassistant.util.dt import utcnow
|
from homeassistant.util.dt import utcnow
|
||||||
|
|
||||||
@ -231,11 +233,34 @@ async def test_send_text_command_expired_token_refresh_failure(
|
|||||||
{"command": "turn on tv"},
|
{"command": "turn on tv"},
|
||||||
blocking=True,
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
|
|
||||||
assert any(entry.async_get_active_flows(hass, {"reauth"})) == requires_reauth
|
assert any(entry.async_get_active_flows(hass, {"reauth"})) == requires_reauth
|
||||||
|
|
||||||
|
|
||||||
|
async def test_send_text_command_grpc_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
setup_integration: ComponentSetup,
|
||||||
|
) -> None:
|
||||||
|
"""Test service call send_text_command when RpcError is raised."""
|
||||||
|
await setup_integration()
|
||||||
|
|
||||||
|
command = "turn on home assistant unsupported device"
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.google_assistant_sdk.helpers.TextAssistant.assist",
|
||||||
|
side_effect=RpcError(),
|
||||||
|
) as mock_assist_call,
|
||||||
|
pytest.raises(HomeAssistantError),
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
DOMAIN,
|
||||||
|
"send_text_command",
|
||||||
|
{"command": command},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
mock_assist_call.assert_called_once_with(command)
|
||||||
|
|
||||||
|
|
||||||
async def test_send_text_command_media_player(
|
async def test_send_text_command_media_player(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
setup_integration: ComponentSetup,
|
setup_integration: ComponentSetup,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
from unittest.mock import call, patch
|
from unittest.mock import call, patch
|
||||||
|
|
||||||
|
from grpc import RpcError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import notify
|
from homeassistant.components import notify
|
||||||
@ -9,6 +10,7 @@ from homeassistant.components.google_assistant_sdk import DOMAIN
|
|||||||
from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES
|
from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES
|
||||||
from homeassistant.components.google_assistant_sdk.notify import broadcast_commands
|
from homeassistant.components.google_assistant_sdk.notify import broadcast_commands
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
|
from homeassistant.exceptions import HomeAssistantError
|
||||||
|
|
||||||
from .conftest import ComponentSetup, ExpectedCredentials
|
from .conftest import ComponentSetup, ExpectedCredentials
|
||||||
|
|
||||||
@ -45,8 +47,8 @@ async def test_broadcast_no_targets(
|
|||||||
notify.DOMAIN,
|
notify.DOMAIN,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
{notify.ATTR_MESSAGE: message},
|
{notify.ATTR_MESSAGE: message},
|
||||||
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
mock_text_assistant.assert_called_once_with(
|
mock_text_assistant.assert_called_once_with(
|
||||||
ExpectedCredentials(), language_code, audio_out=False
|
ExpectedCredentials(), language_code, audio_out=False
|
||||||
)
|
)
|
||||||
@ -54,6 +56,30 @@ async def test_broadcast_no_targets(
|
|||||||
mock_text_assistant.assert_has_calls([call().__enter__().assist(expected_command)])
|
mock_text_assistant.assert_has_calls([call().__enter__().assist(expected_command)])
|
||||||
|
|
||||||
|
|
||||||
|
async def test_broadcast_grpc_error(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
setup_integration: ComponentSetup,
|
||||||
|
) -> None:
|
||||||
|
"""Test broadcast handling when RpcError is raised."""
|
||||||
|
await setup_integration()
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch(
|
||||||
|
"homeassistant.components.google_assistant_sdk.helpers.TextAssistant.assist",
|
||||||
|
side_effect=RpcError(),
|
||||||
|
) as mock_assist_call,
|
||||||
|
pytest.raises(HomeAssistantError),
|
||||||
|
):
|
||||||
|
await hass.services.async_call(
|
||||||
|
notify.DOMAIN,
|
||||||
|
DOMAIN,
|
||||||
|
{notify.ATTR_MESSAGE: "Dinner is served"},
|
||||||
|
blocking=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
mock_assist_call.assert_called_once_with("broadcast Dinner is served")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
("language_code", "message", "target", "expected_command"),
|
("language_code", "message", "target", "expected_command"),
|
||||||
[
|
[
|
||||||
@ -103,8 +129,8 @@ async def test_broadcast_one_target(
|
|||||||
notify.DOMAIN,
|
notify.DOMAIN,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
{notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target]},
|
{notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target]},
|
||||||
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
mock_assist_call.assert_called_once_with(expected_command)
|
mock_assist_call.assert_called_once_with(expected_command)
|
||||||
|
|
||||||
|
|
||||||
@ -127,8 +153,8 @@ async def test_broadcast_two_targets(
|
|||||||
notify.DOMAIN,
|
notify.DOMAIN,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
{notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target1, target2]},
|
{notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target1, target2]},
|
||||||
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
mock_assist_call.assert_has_calls(
|
mock_assist_call.assert_has_calls(
|
||||||
[call(expected_command1), call(expected_command2)]
|
[call(expected_command1), call(expected_command2)]
|
||||||
)
|
)
|
||||||
@ -148,8 +174,8 @@ async def test_broadcast_empty_message(
|
|||||||
notify.DOMAIN,
|
notify.DOMAIN,
|
||||||
DOMAIN,
|
DOMAIN,
|
||||||
{notify.ATTR_MESSAGE: ""},
|
{notify.ATTR_MESSAGE: ""},
|
||||||
|
blocking=True,
|
||||||
)
|
)
|
||||||
await hass.async_block_till_done()
|
|
||||||
mock_assist_call.assert_not_called()
|
mock_assist_call.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user