Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support for Actions on Google Assistant Smart Home Control."""
2 
3 from __future__ import annotations
4 
5 import logging
6 
7 import voluptuous as vol
8 
9 from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
10 from homeassistant.const import CONF_API_KEY, CONF_NAME, Platform
11 from homeassistant.core import HomeAssistant, ServiceCall
12 from homeassistant.helpers import config_validation as cv, device_registry as dr
13 from homeassistant.helpers.typing import ConfigType
14 
15 from .const import ( # noqa: F401
16  CONF_ALIASES,
17  CONF_CLIENT_EMAIL,
18  CONF_ENTITY_CONFIG,
19  CONF_EXPOSE,
20  CONF_EXPOSE_BY_DEFAULT,
21  CONF_EXPOSED_DOMAINS,
22  CONF_PRIVATE_KEY,
23  CONF_PROJECT_ID,
24  CONF_REPORT_STATE,
25  CONF_ROOM_HINT,
26  CONF_SECURE_DEVICES_PIN,
27  CONF_SERVICE_ACCOUNT,
28  DATA_CONFIG,
29  DEFAULT_EXPOSE_BY_DEFAULT,
30  DEFAULT_EXPOSED_DOMAINS,
31  DOMAIN,
32  EVENT_QUERY_RECEIVED,
33  SERVICE_REQUEST_SYNC,
34  SOURCE_CLOUD,
35 )
36 from .http import GoogleAssistantView, GoogleConfig
37 
38 from .const import EVENT_COMMAND_RECEIVED, EVENT_SYNC_RECEIVED # noqa: F401, isort:skip
39 
40 _LOGGER = logging.getLogger(__name__)
41 
42 CONF_ALLOW_UNLOCK = "allow_unlock"
43 
44 PLATFORMS = [Platform.BUTTON]
45 
46 ENTITY_SCHEMA = vol.Schema(
47  {
48  vol.Optional(CONF_NAME): cv.string,
49  vol.Optional(CONF_EXPOSE, default=True): cv.boolean,
50  vol.Optional(CONF_ALIASES): vol.All(cv.ensure_list, [cv.string]),
51  vol.Optional(CONF_ROOM_HINT): cv.string,
52  }
53 )
54 
55 GOOGLE_SERVICE_ACCOUNT = vol.Schema(
56  {
57  vol.Required(CONF_PRIVATE_KEY): cv.string,
58  vol.Required(CONF_CLIENT_EMAIL): cv.string,
59  },
60  extra=vol.ALLOW_EXTRA,
61 )
62 
63 
65  if data[CONF_REPORT_STATE] and CONF_SERVICE_ACCOUNT not in data:
66  raise vol.Invalid("If report state is enabled, a service account must exist")
67  return data
68 
69 
70 GOOGLE_ASSISTANT_SCHEMA = vol.All(
71  vol.Schema(
72  {
73  vol.Required(CONF_PROJECT_ID): cv.string,
74  vol.Optional(
75  CONF_EXPOSE_BY_DEFAULT, default=DEFAULT_EXPOSE_BY_DEFAULT
76  ): cv.boolean,
77  vol.Optional(
78  CONF_EXPOSED_DOMAINS, default=DEFAULT_EXPOSED_DOMAINS
79  ): cv.ensure_list,
80  vol.Optional(CONF_ENTITY_CONFIG): {cv.entity_id: ENTITY_SCHEMA},
81  # str on purpose, makes sure it is configured correctly.
82  vol.Optional(CONF_SECURE_DEVICES_PIN): str,
83  vol.Optional(CONF_REPORT_STATE, default=False): cv.boolean,
84  vol.Optional(CONF_SERVICE_ACCOUNT): GOOGLE_SERVICE_ACCOUNT,
85  # deprecated configuration options
86  vol.Remove(CONF_ALLOW_UNLOCK): cv.boolean,
87  vol.Remove(CONF_API_KEY): cv.string,
88  },
89  extra=vol.PREVENT_EXTRA,
90  ),
91  _check_report_state,
92 )
93 
94 CONFIG_SCHEMA = vol.Schema(
95  {vol.Optional(DOMAIN): GOOGLE_ASSISTANT_SCHEMA}, extra=vol.ALLOW_EXTRA
96 )
97 
98 
99 async def async_setup(hass: HomeAssistant, yaml_config: ConfigType) -> bool:
100  """Activate Google Actions component."""
101  if DOMAIN not in yaml_config:
102  return True
103 
104  hass.data[DOMAIN] = {}
105  hass.data[DOMAIN][DATA_CONFIG] = yaml_config[DOMAIN]
106 
107  hass.async_create_task(
108  hass.config_entries.flow.async_init(
109  DOMAIN,
110  context={"source": SOURCE_IMPORT},
111  data={CONF_PROJECT_ID: yaml_config[DOMAIN][CONF_PROJECT_ID]},
112  )
113  )
114 
115  return True
116 
117 
118 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
119  """Set up from a config entry."""
120 
121  config: ConfigType = {**hass.data[DOMAIN][DATA_CONFIG]}
122 
123  if entry.source == SOURCE_IMPORT:
124  # if project was changed, remove entry a new will be setup
125  if config[CONF_PROJECT_ID] != entry.data[CONF_PROJECT_ID]:
126  hass.async_create_task(hass.config_entries.async_remove(entry.entry_id))
127  return False
128 
129  config.update(entry.data)
130 
131  device_registry = dr.async_get(hass)
132  device_registry.async_get_or_create(
133  config_entry_id=entry.entry_id,
134  identifiers={(DOMAIN, config[CONF_PROJECT_ID])},
135  manufacturer="Google",
136  model="Google Assistant",
137  name=config[CONF_PROJECT_ID],
138  entry_type=dr.DeviceEntryType.SERVICE,
139  )
140 
141  google_config = GoogleConfig(hass, config)
142  await google_config.async_initialize()
143 
144  hass.data[DOMAIN][entry.entry_id] = google_config
145 
146  hass.http.register_view(GoogleAssistantView(google_config))
147 
148  if google_config.should_report_state:
149  google_config.async_enable_report_state()
150 
151  async def request_sync_service_handler(call: ServiceCall) -> None:
152  """Handle request sync service calls."""
153  agent_user_id = call.data.get("agent_user_id") or call.context.user_id
154 
155  if agent_user_id is None:
156  _LOGGER.warning(
157  "No agent_user_id supplied for request_sync. Call as a user or pass in"
158  " user id as agent_user_id"
159  )
160  return
161 
162  await google_config.async_sync_entities(agent_user_id)
163 
164  # Register service only if key is provided
165  if CONF_SERVICE_ACCOUNT in config:
166  hass.services.async_register(
167  DOMAIN, SERVICE_REQUEST_SYNC, request_sync_service_handler
168  )
169 
170  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
171 
172  return True
bool async_setup(HomeAssistant hass, ConfigType yaml_config)
Definition: __init__.py:99
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:118