Files
core/homeassistant/components/cloud/http_api.py
T

203 lines
5.9 KiB
Python
Raw Normal View History

2017-08-29 13:40:08 -07:00
"""The HTTP api to control the cloud integration."""
import asyncio
2017-09-12 09:47:04 -07:00
from functools import wraps
2017-08-29 13:40:08 -07:00
import logging
import async_timeout
2018-01-21 07:35:38 +01:00
import voluptuous as vol
2017-08-29 13:40:08 -07:00
from homeassistant.components.http import HomeAssistantView
from homeassistant.components.http.data_validator import (
RequestDataValidator)
2017-08-29 13:40:08 -07:00
2017-09-12 09:47:04 -07:00
from . import auth_api
2017-10-14 19:43:14 -07:00
from .const import DOMAIN, REQUEST_TIMEOUT
2017-08-29 13:40:08 -07:00
_LOGGER = logging.getLogger(__name__)
@asyncio.coroutine
def async_setup(hass):
2018-01-21 07:35:38 +01:00
"""Initialize the HTTP API."""
2017-08-29 13:40:08 -07:00
hass.http.register_view(CloudLoginView)
hass.http.register_view(CloudLogoutView)
hass.http.register_view(CloudAccountView)
2017-09-12 09:47:04 -07:00
hass.http.register_view(CloudRegisterView)
hass.http.register_view(CloudResendConfirmView)
2017-09-12 09:47:04 -07:00
hass.http.register_view(CloudForgotPasswordView)
_CLOUD_ERRORS = {
auth_api.UserNotFound: (400, "User does not exist."),
auth_api.UserNotConfirmed: (400, 'Email not confirmed.'),
auth_api.Unauthenticated: (401, 'Authentication failed.'),
auth_api.PasswordChangeRequired: (400, 'Password change required.'),
asyncio.TimeoutError: (502, 'Unable to reach the Home Assistant cloud.')
}
def _handle_cloud_errors(handler):
2018-01-21 07:35:38 +01:00
"""Handle auth errors."""
2017-09-12 09:47:04 -07:00
@asyncio.coroutine
@wraps(handler)
def error_handler(view, request, *args, **kwargs):
"""Handle exceptions that raise from the wrapped request handler."""
try:
result = yield from handler(view, request, *args, **kwargs)
return result
except (auth_api.CloudError, asyncio.TimeoutError) as err:
err_info = _CLOUD_ERRORS.get(err.__class__)
if err_info is None:
err_info = (502, 'Unexpected error: {}'.format(err))
status, msg = err_info
return view.json_message(msg, status_code=status,
message_code=err.__class__.__name__)
return error_handler
2017-08-29 13:40:08 -07:00
class CloudLoginView(HomeAssistantView):
"""Login to Home Assistant cloud."""
url = '/api/cloud/login'
name = 'api:cloud:login'
2017-09-12 09:47:04 -07:00
@_handle_cloud_errors
@RequestDataValidator(vol.Schema({
vol.Required('email'): str,
vol.Required('password'): str,
}))
2017-11-20 21:44:22 -08:00
@asyncio.coroutine
2017-09-12 09:47:04 -07:00
def post(self, request, data):
"""Handle login request."""
2017-08-29 13:40:08 -07:00
hass = request.app['hass']
2017-10-14 19:43:14 -07:00
cloud = hass.data[DOMAIN]
2017-08-29 13:40:08 -07:00
2017-09-12 09:47:04 -07:00
with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop):
2017-10-14 19:43:14 -07:00
yield from hass.async_add_job(auth_api.login, cloud, data['email'],
2017-09-12 09:47:04 -07:00
data['password'])
2017-08-29 13:40:08 -07:00
2017-11-14 23:16:19 -08:00
hass.async_add_job(cloud.iot.connect)
# Allow cloud to start connecting.
yield from asyncio.sleep(0, loop=hass.loop)
2017-10-14 19:43:14 -07:00
return self.json(_account_data(cloud))
2017-08-29 13:40:08 -07:00
class CloudLogoutView(HomeAssistantView):
"""Log out of the Home Assistant cloud."""
url = '/api/cloud/logout'
name = 'api:cloud:logout'
2017-09-12 09:47:04 -07:00
@_handle_cloud_errors
2017-11-20 21:44:22 -08:00
@asyncio.coroutine
2017-08-29 13:40:08 -07:00
def post(self, request):
2017-09-12 09:47:04 -07:00
"""Handle logout request."""
2017-08-29 13:40:08 -07:00
hass = request.app['hass']
2017-10-14 19:43:14 -07:00
cloud = hass.data[DOMAIN]
2017-08-29 13:40:08 -07:00
2017-09-12 09:47:04 -07:00
with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop):
2017-10-14 19:43:14 -07:00
yield from cloud.logout()
2017-08-29 13:40:08 -07:00
2017-09-12 09:47:04 -07:00
return self.json_message('ok')
2017-08-29 13:40:08 -07:00
class CloudAccountView(HomeAssistantView):
2017-09-12 09:47:04 -07:00
"""View to retrieve account info."""
2017-08-29 13:40:08 -07:00
url = '/api/cloud/account'
name = 'api:cloud:account'
@asyncio.coroutine
def get(self, request):
2017-09-12 09:47:04 -07:00
"""Get account info."""
2017-08-29 13:40:08 -07:00
hass = request.app['hass']
2017-10-14 19:43:14 -07:00
cloud = hass.data[DOMAIN]
2017-08-29 13:40:08 -07:00
2017-10-14 19:43:14 -07:00
if not cloud.is_logged_in:
2017-08-29 13:40:08 -07:00
return self.json_message('Not logged in', 400)
2017-10-14 19:43:14 -07:00
return self.json(_account_data(cloud))
2017-09-12 09:47:04 -07:00
class CloudRegisterView(HomeAssistantView):
"""Register on the Home Assistant cloud."""
url = '/api/cloud/register'
name = 'api:cloud:register'
@_handle_cloud_errors
@RequestDataValidator(vol.Schema({
vol.Required('email'): str,
vol.Required('password'): vol.All(str, vol.Length(min=6)),
}))
2017-11-20 21:44:22 -08:00
@asyncio.coroutine
2017-09-12 09:47:04 -07:00
def post(self, request, data):
"""Handle registration request."""
hass = request.app['hass']
2017-10-14 19:43:14 -07:00
cloud = hass.data[DOMAIN]
2017-09-12 09:47:04 -07:00
with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop):
yield from hass.async_add_job(
2017-10-14 19:43:14 -07:00
auth_api.register, cloud, data['email'], data['password'])
2017-09-12 09:47:04 -07:00
return self.json_message('ok')
class CloudResendConfirmView(HomeAssistantView):
"""Resend email confirmation code."""
url = '/api/cloud/resend_confirm'
name = 'api:cloud:resend_confirm'
@_handle_cloud_errors
@RequestDataValidator(vol.Schema({
vol.Required('email'): str,
}))
@asyncio.coroutine
def post(self, request, data):
"""Handle resending confirm email code request."""
hass = request.app['hass']
cloud = hass.data[DOMAIN]
with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop):
yield from hass.async_add_job(
auth_api.resend_email_confirm, cloud, data['email'])
return self.json_message('ok')
2017-09-12 09:47:04 -07:00
class CloudForgotPasswordView(HomeAssistantView):
"""View to start Forgot Password flow.."""
url = '/api/cloud/forgot_password'
name = 'api:cloud:forgot_password'
@_handle_cloud_errors
@RequestDataValidator(vol.Schema({
vol.Required('email'): str,
}))
2017-11-20 21:44:22 -08:00
@asyncio.coroutine
2017-09-12 09:47:04 -07:00
def post(self, request, data):
"""Handle forgot password request."""
hass = request.app['hass']
2017-10-14 19:43:14 -07:00
cloud = hass.data[DOMAIN]
2017-09-12 09:47:04 -07:00
with async_timeout.timeout(REQUEST_TIMEOUT, loop=hass.loop):
yield from hass.async_add_job(
2017-10-14 19:43:14 -07:00
auth_api.forgot_password, cloud, data['email'])
2017-09-12 09:47:04 -07:00
return self.json_message('ok')
2017-10-14 19:43:14 -07:00
def _account_data(cloud):
2017-09-12 09:47:04 -07:00
"""Generate the auth data JSON response."""
2017-11-14 23:16:19 -08:00
claims = cloud.claims
2017-09-12 09:47:04 -07:00
return {
2017-11-14 23:16:19 -08:00
'email': claims['email'],
2018-01-03 10:16:59 -08:00
'sub_exp': claims['custom:sub-exp'],
2017-11-14 23:16:19 -08:00
'cloud': cloud.iot.state,
2017-09-12 09:47:04 -07:00
}