1 """Support for alexa Smart Home Skill API."""
6 from aiohttp
import web
9 from homeassistant
import core
21 from .auth
import Auth
22 from .config
import AbstractConfig
30 EVENT_ALEXA_SMART_HOME,
32 from .diagnostics
import async_redact_auth_data
33 from .errors
import AlexaBridgeUnreachableError, AlexaError
34 from .handlers
import HANDLERS
35 from .state_report
import AlexaDirective
37 _LOGGER = logging.getLogger(__name__)
38 SMART_HOME_HTTP_ENDPOINT =
"/api/alexa/smart_home"
46 def __init__(self, hass: HomeAssistant, config: ConfigType) ->
None:
47 """Initialize Alexa config."""
51 if config.get(CONF_CLIENT_ID)
and config.get(CONF_CLIENT_SECRET):
52 self.
_auth_auth =
Auth(hass, config[CONF_CLIENT_ID], config[CONF_CLIENT_SECRET])
54 self.
_auth_auth =
None
58 """Return if config supports auth."""
59 return self.
_auth_auth
is not None
63 """Return if we should proactively report states."""
68 """Endpoint for report state."""
73 """Return entity config."""
74 return self.
_config_config.
get(CONF_ENTITY_CONFIG)
or {}
78 """Return config locale."""
83 """Return an identifier for the user that represents this config."""
88 """If an entity should be exposed."""
89 if not self.
_config_config[CONF_FILTER].empty_filter:
90 return bool(self.
_config_config[CONF_FILTER](entity_id))
92 entity_registry = er.async_get(self.
hasshass)
93 if registry_entry := entity_registry.async_get(entity_id):
95 registry_entry.entity_category
is not None
96 or registry_entry.hidden_by
is not None
99 auxiliary_entity =
False
100 return not auxiliary_entity
104 """Invalidate access token."""
105 assert self.
_auth_auth
is not None
109 """Get an access token."""
110 assert self.
_auth_auth
is not None
114 """Accept a grant."""
115 assert self.
_auth_auth
is not None
116 return await self.
_auth_auth.async_do_auth(code)
119 async
def async_setup(hass: HomeAssistant, config: ConfigType) ->
None:
120 """Activate Smart Home functionality of Alexa component.
122 This is optional, triggered by having a `smart_home:` sub-section in the
125 Even if that's disabled, the functionality in this module may still be used
126 by the cloud component which will call async_handle_message directly.
129 await smart_home_config.async_initialize()
132 if smart_home_config.should_report_state:
133 await smart_home_config.async_enable_proactive_mode()
137 """Expose Smart Home v3 payload interface via HTTP POST."""
139 url = SMART_HOME_HTTP_ENDPOINT
140 name =
"api:alexa:smart_home"
142 def __init__(self, smart_home_config: AlexaConfig) ->
None:
146 async
def post(self, request: HomeAssistantRequest) -> web.Response | bytes:
147 """Handle Alexa Smart Home requests.
149 The Smart Home API requires the endpoint to be implemented in AWS
150 Lambda, which will need to forward the requests to here and pass back
153 hass = request.app[KEY_HASS]
154 user: User = request[
"hass_user"]
155 message: dict[str, Any] = await request.json()
157 if _LOGGER.isEnabledFor(logging.DEBUG):
159 "Received Alexa Smart Home request: %s",
166 if _LOGGER.isEnabledFor(logging.DEBUG):
168 "Sending Alexa Smart Home response: %s",
172 return b
"" if response
is None else self.json(response)
177 config: AbstractConfig,
178 request: dict[str, Any],
179 context: Context |
None =
None,
180 enabled: bool =
True,
182 """Handle incoming API messages.
184 If enabled is False, the response to all messages will be a
185 BRIDGE_UNREACHABLE error. This can be used if the API has been disabled in
188 assert request[API_DIRECTIVE][API_HEADER][
"payloadVersion"] ==
"3"
198 "Alexa API not enabled in Home Assistant configuration"
201 await config.set_authorized(
True)
203 if directive.has_endpoint:
204 directive.load_entity(hass, config)
206 funct_ref = HANDLERS.get((directive.namespace, directive.name))
208 response = await funct_ref(hass, config, directive, context)
209 if directive.has_endpoint:
210 response.merge_context_properties(directive.endpoint)
213 "Unsupported API request %s/%s", directive.namespace, directive.name
215 response = directive.error()
216 except AlexaError
as err:
217 response = directive.error(
218 error_type=
str(err.error_type),
219 error_message=err.error_message,
224 "Uncaught exception processing Alexa %s/%s request (%s)",
227 directive.entity_id
or "-",
229 response = directive.error(error_message=
"Unknown error")
231 request_info: dict[str, Any] = {
232 "namespace": directive.namespace,
233 "name": directive.name,
236 if directive.has_endpoint:
237 assert directive.entity_id
is not None
238 request_info[
"entity_id"] = directive.entity_id
241 EVENT_ALEXA_SMART_HOME,
243 "request": request_info,
244 "response": {
"namespace": response.namespace,
"name": response.name},
249 return response.serialize()
str|URL|None endpoint(self)
bool should_report_state(self)
bool should_expose(self, str entity_id)
None async_invalidate_access_token(self)
str user_identifier(self)
str|None async_get_access_token(self)
str|None async_accept_grant(self, str code)
dict[str, Any] entity_config(self)
None __init__(self, HomeAssistant hass, ConfigType config)
None __init__(self, AlexaConfig smart_home_config)
web.Response|bytes post(self, HomeAssistantRequest request)
dict[str, str] async_redact_auth_data(Mapping[Any, Any] mapping)
dict[str, Any] async_handle_message(HomeAssistant hass, AbstractConfig config, dict[str, Any] request, Context|None context=None, bool enabled=True)
None async_setup(HomeAssistant hass, ConfigType config)
web.Response get(self, web.Request request, str config_key)