Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The Google Generative AI Conversation integration."""
2 
3 from __future__ import annotations
4 
5 import mimetypes
6 from pathlib import Path
7 
8 from google.ai import generativelanguage_v1beta
9 from google.api_core.client_options import ClientOptions
10 from google.api_core.exceptions import ClientError, DeadlineExceeded, GoogleAPIError
11 import google.generativeai as genai
12 import google.generativeai.types as genai_types
13 import voluptuous as vol
14 
15 from homeassistant.config_entries import ConfigEntry
16 from homeassistant.const import CONF_API_KEY, Platform
17 from homeassistant.core import (
18  HomeAssistant,
19  ServiceCall,
20  ServiceResponse,
21  SupportsResponse,
22 )
23 from homeassistant.exceptions import (
24  ConfigEntryAuthFailed,
25  ConfigEntryError,
26  ConfigEntryNotReady,
27  HomeAssistantError,
28 )
29 from homeassistant.helpers import config_validation as cv
30 from homeassistant.helpers.typing import ConfigType
31 
32 from .const import CONF_CHAT_MODEL, CONF_PROMPT, DOMAIN, RECOMMENDED_CHAT_MODEL
33 
34 SERVICE_GENERATE_CONTENT = "generate_content"
35 CONF_IMAGE_FILENAME = "image_filename"
36 
37 CONFIG_SCHEMA = cv.config_entry_only_config_schema(DOMAIN)
38 PLATFORMS = (Platform.CONVERSATION,)
39 
40 
41 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
42  """Set up Google Generative AI Conversation."""
43 
44  async def generate_content(call: ServiceCall) -> ServiceResponse:
45  """Generate content from text and optionally images."""
46  prompt_parts = [call.data[CONF_PROMPT]]
47  image_filenames = call.data[CONF_IMAGE_FILENAME]
48  for image_filename in image_filenames:
49  if not hass.config.is_allowed_path(image_filename):
50  raise HomeAssistantError(
51  f"Cannot read `{image_filename}`, no access to path; "
52  "`allowlist_external_dirs` may need to be adjusted in "
53  "`configuration.yaml`"
54  )
55  if not Path(image_filename).exists():
56  raise HomeAssistantError(f"`{image_filename}` does not exist")
57  mime_type, _ = mimetypes.guess_type(image_filename)
58  if mime_type is None or not mime_type.startswith("image"):
59  raise HomeAssistantError(f"`{image_filename}` is not an image")
60  prompt_parts.append(
61  {
62  "mime_type": mime_type,
63  "data": await hass.async_add_executor_job(
64  Path(image_filename).read_bytes
65  ),
66  }
67  )
68 
69  model = genai.GenerativeModel(model_name=RECOMMENDED_CHAT_MODEL)
70 
71  try:
72  response = await model.generate_content_async(prompt_parts)
73  except (
74  GoogleAPIError,
75  ValueError,
76  genai_types.BlockedPromptException,
77  genai_types.StopCandidateException,
78  ) as err:
79  raise HomeAssistantError(f"Error generating content: {err}") from err
80 
81  if not response.parts:
82  raise HomeAssistantError("Error generating content")
83 
84  return {"text": response.text}
85 
86  hass.services.async_register(
87  DOMAIN,
88  SERVICE_GENERATE_CONTENT,
89  generate_content,
90  schema=vol.Schema(
91  {
92  vol.Required(CONF_PROMPT): cv.string,
93  vol.Optional(CONF_IMAGE_FILENAME, default=[]): vol.All(
94  cv.ensure_list, [cv.string]
95  ),
96  }
97  ),
98  supports_response=SupportsResponse.ONLY,
99  )
100  return True
101 
102 
103 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
104  """Set up Google Generative AI Conversation from a config entry."""
105  genai.configure(api_key=entry.data[CONF_API_KEY])
106 
107  try:
108  client = generativelanguage_v1beta.ModelServiceAsyncClient(
109  client_options=ClientOptions(api_key=entry.data[CONF_API_KEY])
110  )
111  await client.get_model(
112  name=entry.options.get(CONF_CHAT_MODEL, RECOMMENDED_CHAT_MODEL), timeout=5.0
113  )
114  except (GoogleAPIError, ValueError) as err:
115  if isinstance(err, ClientError) and err.reason == "API_KEY_INVALID":
116  raise ConfigEntryAuthFailed(err) from err
117  if isinstance(err, DeadlineExceeded):
118  raise ConfigEntryNotReady(err) from err
119  raise ConfigEntryError(err) from err
120 
121  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
122 
123  return True
124 
125 
126 async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
127  """Unload GoogleGenerativeAI."""
128  if not await hass.config_entries.async_unload_platforms(entry, PLATFORMS):
129  return False
130 
131  return True
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:103
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:41
bool async_unload_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:126