Phil Kates 9d20a53d63 Google Actions for Assistant (#9632)
* http: Add headers key to json[_message]

* Add google_assistant component

This component provides API endpoints for the Actions on Google Smart
Home API to interact with Google Assistant.

* google_assistant: Re-add fan support

* google_assistant: Fix Scene handling

- The way I originally wrote the MAPPING_COMPONENT and the way it's actual
  used changed so the comment was updated to match that.
- Use const's in more places
- Handle the ActivateScene command correctly

* google_assistant: Fix flakey compare test

Was failing on 3.4.2 and 3.5, this is more correct anyway.

* google_assistant: Use volume attr for media_player
2017-10-17 22:00:59 -07:00

87 lines
2.8 KiB
Python

"""Google Assistant OAuth View."""
import asyncio
import logging
# Typing imports
# pylint: disable=using-constant-test,unused-import,ungrouped-imports
# if False:
from homeassistant.core import HomeAssistant # NOQA
from aiohttp.web import Request, Response # NOQA
from typing import Dict, Any # NOQA
from homeassistant.components.http import HomeAssistantView
from homeassistant.const import (
HTTP_BAD_REQUEST,
HTTP_UNAUTHORIZED,
HTTP_MOVED_PERMANENTLY,
)
from .const import (
GOOGLE_ASSISTANT_API_ENDPOINT,
CONF_PROJECT_ID, CONF_CLIENT_ID, CONF_ACCESS_TOKEN
)
BASE_OAUTH_URL = 'https://oauth-redirect.googleusercontent.com'
REDIRECT_TEMPLATE_URL = \
'{}/r/{}#access_token={}&token_type=bearer&state={}'
_LOGGER = logging.getLogger(__name__)
class GoogleAssistantAuthView(HomeAssistantView):
"""Handle Google Actions auth requests."""
url = GOOGLE_ASSISTANT_API_ENDPOINT + '/auth'
name = 'api:google_assistant:auth'
requires_auth = False
def __init__(self, hass: HomeAssistant, cfg: Dict[str, Any]) -> None:
"""Initialize instance of the view."""
super().__init__()
self.project_id = cfg.get(CONF_PROJECT_ID)
self.client_id = cfg.get(CONF_CLIENT_ID)
self.access_token = cfg.get(CONF_ACCESS_TOKEN)
@asyncio.coroutine
def get(self, request: Request) -> Response:
"""Handle oauth token request."""
query = request.query
redirect_uri = query.get('redirect_uri')
if not redirect_uri:
msg = 'missing redirect_uri field'
_LOGGER.warning(msg)
return self.json_message(msg, status_code=HTTP_BAD_REQUEST)
if self.project_id not in redirect_uri:
msg = 'missing project_id in redirect_uri'
_LOGGER.warning(msg)
return self.json_message(msg, status_code=HTTP_BAD_REQUEST)
state = query.get('state')
if not state:
msg = 'oauth request missing state'
_LOGGER.warning(msg)
return self.json_message(msg, status_code=HTTP_BAD_REQUEST)
client_id = query.get('client_id')
if self.client_id != client_id:
msg = 'invalid client id'
_LOGGER.warning(msg)
return self.json_message(msg, status_code=HTTP_UNAUTHORIZED)
generated_url = redirect_url(self.project_id, self.access_token, state)
_LOGGER.info('user login in from Google Assistant')
return self.json_message(
'redirect success',
status_code=HTTP_MOVED_PERMANENTLY,
headers={'Location': generated_url})
def redirect_url(project_id: str, access_token: str, state: str) -> str:
"""Generate the redirect format for the oauth request."""
return REDIRECT_TEMPLATE_URL.format(BASE_OAUTH_URL, project_id,
access_token, state)