Handle grpc errors in Google Assistant SDK (#146438)

pull/146456/head
tronikos 2025-06-10 06:31:32 -07:00 committed by GitHub
parent 7da1671b06
commit b9e8cfb291
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 74 additions and 6 deletions

View File

@ -12,6 +12,7 @@ import aiohttp
from aiohttp import web
from gassist_text import TextAssistant
from google.oauth2.credentials import Credentials
from grpc import RpcError
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.media_player import (
@ -25,6 +26,7 @@ from homeassistant.components.media_player import (
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_ENTITY_ID, CONF_ACCESS_TOKEN
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers.config_entry_oauth2_flow import OAuth2Session
from homeassistant.helpers.event import async_call_later
@ -83,7 +85,17 @@ async def async_send_text_commands(
) as assistant:
command_response_list = []
for command in commands:
resp = await hass.async_add_executor_job(assistant.assist, command)
try:
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]
_LOGGER.debug("command: %s\nresponse: %s", command, text_response)
audio_response = resp[2]

View File

@ -57,5 +57,10 @@
}
}
}
},
"exceptions": {
"grpc_error": {
"message": "Failed to communicate with Google Assistant"
}
}
}

View File

@ -6,6 +6,7 @@ import time
from unittest.mock import call, patch
import aiohttp
from grpc import RpcError
import pytest
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.config_entries import ConfigEntryState
from homeassistant.core import Context, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
from homeassistant.util.dt import utcnow
@ -231,11 +233,34 @@ async def test_send_text_command_expired_token_refresh_failure(
{"command": "turn on tv"},
blocking=True,
)
await hass.async_block_till_done()
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(
hass: HomeAssistant,
setup_integration: ComponentSetup,

View File

@ -2,6 +2,7 @@
from unittest.mock import call, patch
from grpc import RpcError
import pytest
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.notify import broadcast_commands
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from .conftest import ComponentSetup, ExpectedCredentials
@ -45,8 +47,8 @@ async def test_broadcast_no_targets(
notify.DOMAIN,
DOMAIN,
{notify.ATTR_MESSAGE: message},
blocking=True,
)
await hass.async_block_till_done()
mock_text_assistant.assert_called_once_with(
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)])
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(
("language_code", "message", "target", "expected_command"),
[
@ -103,8 +129,8 @@ async def test_broadcast_one_target(
notify.DOMAIN,
DOMAIN,
{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)
@ -127,8 +153,8 @@ async def test_broadcast_two_targets(
notify.DOMAIN,
DOMAIN,
{notify.ATTR_MESSAGE: message, notify.ATTR_TARGET: [target1, target2]},
blocking=True,
)
await hass.async_block_till_done()
mock_assist_call.assert_has_calls(
[call(expected_command1), call(expected_command2)]
)
@ -148,8 +174,8 @@ async def test_broadcast_empty_message(
notify.DOMAIN,
DOMAIN,
{notify.ATTR_MESSAGE: ""},
blocking=True,
)
await hass.async_block_till_done()
mock_assist_call.assert_not_called()