1 """Helper classes for Google Assistant SDK integration."""
3 from __future__
import annotations
5 from dataclasses
import dataclass
6 from http
import HTTPStatus
12 from aiohttp
import web
13 from gassist_text
import TextAssistant
14 from google.oauth2.credentials
import Credentials
19 ATTR_MEDIA_CONTENT_ID,
20 ATTR_MEDIA_CONTENT_TYPE,
36 SUPPORTED_LANGUAGE_CODES,
39 _LOGGER = logging.getLogger(__name__)
41 DEFAULT_LANGUAGE_CODES = {
55 """Response from a single command to Google Assistant Service."""
61 hass: HomeAssistant, commands: list[str], media_players: list[str] |
None =
None
62 ) -> list[CommandResponse]:
63 """Send text commands to Google Assistant Service."""
65 entry: ConfigEntry = hass.config_entries.async_entries(DOMAIN)[0]
67 session: OAuth2Session = hass.data[DOMAIN][entry.entry_id][DATA_SESSION]
69 await session.async_ensure_token_valid()
70 except aiohttp.ClientResponseError
as err:
71 if 400 <= err.status < 500:
72 entry.async_start_reauth(hass)
75 credentials = Credentials(session.token[CONF_ACCESS_TOKEN])
78 credentials, language_code, audio_out=bool(media_players)
80 command_response_list = []
81 for command
in commands:
82 resp = await hass.async_add_executor_job(assistant.assist, command)
83 text_response = resp[0]
84 _LOGGER.debug(
"command: %s\nresponse: %s", command, text_response)
85 audio_response = resp[2]
86 if media_players
and audio_response:
87 mem_storage: InMemoryStorage = hass.data[DOMAIN][entry.entry_id][
90 audio_url = GoogleAssistantSDKAudioView.url.format(
91 filename=mem_storage.store_and_get_identifier(audio_response)
93 await hass.services.async_call(
97 ATTR_ENTITY_ID: media_players,
98 ATTR_MEDIA_CONTENT_ID: audio_url,
99 ATTR_MEDIA_CONTENT_TYPE: MediaType.MUSIC,
100 ATTR_MEDIA_ANNOUNCE:
True,
105 return command_response_list
109 """Get default language code based on Home Assistant config."""
110 language_code = f
"{hass.config.language}-{hass.config.country}"
111 if language_code
in SUPPORTED_LANGUAGE_CODES:
113 return DEFAULT_LANGUAGE_CODES.get(hass.config.language,
"en-US")
117 hass: HomeAssistant, assist_language: str, agent_language: str |
None =
None
119 """Get the best matching language, based on the preferred assist language and the configured agent language."""
122 if assist_language
in SUPPORTED_LANGUAGE_CODES:
123 return assist_language
124 language = assist_language.split(
"-")[0]
127 if agent_language
is not None and agent_language.startswith(language):
131 default_language = DEFAULT_LANGUAGE_CODES.get(language)
132 if default_language
is not None:
133 return default_language
136 if agent_language
is not None:
144 """Temporarily store and retrieve data from in memory storage."""
147 """Initialize InMemoryStorage."""
148 self.hass: HomeAssistant = hass
149 self.mem: dict[str, bytes] = {}
152 """Temporarily store data and return identifier to be able to retrieve it.
154 Data expires after 5 minutes.
156 identifier: str = uuid.uuid1().hex
157 self.mem[identifier] = data
159 def async_remove_from_mem(*_: Any) ->
None:
160 """Cleanup memory."""
161 self.mem.pop(identifier,
None)
168 def retrieve(self, identifier: str) -> bytes |
None:
169 """Retrieve previously stored data."""
170 return self.mem.
get(identifier)
174 """Google Assistant SDK view to serve audio responses."""
177 url =
"/api/google_assistant_sdk/audio/{filename}"
178 name =
"api:google_assistant_sdk:audio"
180 def __init__(self, mem_storage: InMemoryStorage) ->
None:
181 """Initialize GoogleAssistantSDKView."""
182 self.mem_storage: InMemoryStorage = mem_storage
184 async
def get(self, request: web.Request, filename: str) -> web.Response:
185 """Start a get request."""
186 audio = self.mem_storage.retrieve(filename)
188 return web.Response(status=HTTPStatus.NOT_FOUND)
189 return web.Response(body=audio, content_type=
"audio/mpeg")
None __init__(self, InMemoryStorage mem_storage)
web.Response get(self, web.Request request, str filename)
bytes|None retrieve(self, str identifier)
str store_and_get_identifier(self, bytes data)
None __init__(self, HomeAssistant hass)
web.Response get(self, web.Request request, str config_key)
list[CommandResponse] async_send_text_commands(HomeAssistant hass, list[str] commands, list[str]|None media_players=None)
str default_language_code(HomeAssistant hass)
str best_matching_language_code(HomeAssistant hass, str assist_language, str|None agent_language=None)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)