198 lines
6.1 KiB
Python
198 lines
6.1 KiB
Python
"""Onboarding views."""
|
|
import asyncio
|
|
|
|
import voluptuous as vol
|
|
|
|
from homeassistant.auth.const import GROUP_ID_ADMIN
|
|
from homeassistant.components.http.data_validator import RequestDataValidator
|
|
from homeassistant.components.http.view import HomeAssistantView
|
|
from homeassistant.const import HTTP_FORBIDDEN
|
|
from homeassistant.core import callback
|
|
|
|
from .const import (
|
|
DEFAULT_AREAS,
|
|
DOMAIN,
|
|
STEP_CORE_CONFIG,
|
|
STEP_INTEGRATION,
|
|
STEP_USER,
|
|
STEPS,
|
|
)
|
|
|
|
|
|
async def async_setup(hass, data, store):
|
|
"""Set up the onboarding view."""
|
|
hass.http.register_view(OnboardingView(data, store))
|
|
hass.http.register_view(UserOnboardingView(data, store))
|
|
hass.http.register_view(CoreConfigOnboardingView(data, store))
|
|
hass.http.register_view(IntegrationOnboardingView(data, store))
|
|
|
|
|
|
class OnboardingView(HomeAssistantView):
|
|
"""Return the onboarding status."""
|
|
|
|
requires_auth = False
|
|
url = "/api/onboarding"
|
|
name = "api:onboarding"
|
|
|
|
def __init__(self, data, store):
|
|
"""Initialize the onboarding view."""
|
|
self._store = store
|
|
self._data = data
|
|
|
|
async def get(self, request):
|
|
"""Return the onboarding status."""
|
|
return self.json(
|
|
[{"step": key, "done": key in self._data["done"]} for key in STEPS]
|
|
)
|
|
|
|
|
|
class _BaseOnboardingView(HomeAssistantView):
|
|
"""Base class for onboarding."""
|
|
|
|
step = None
|
|
|
|
def __init__(self, data, store):
|
|
"""Initialize the onboarding view."""
|
|
self._store = store
|
|
self._data = data
|
|
self._lock = asyncio.Lock()
|
|
|
|
@callback
|
|
def _async_is_done(self):
|
|
"""Return if this step is done."""
|
|
return self.step in self._data["done"]
|
|
|
|
async def _async_mark_done(self, hass):
|
|
"""Mark step as done."""
|
|
self._data["done"].append(self.step)
|
|
await self._store.async_save(self._data)
|
|
|
|
if set(self._data["done"]) == set(STEPS):
|
|
hass.data[DOMAIN] = True
|
|
|
|
|
|
class UserOnboardingView(_BaseOnboardingView):
|
|
"""View to handle create user onboarding step."""
|
|
|
|
url = "/api/onboarding/users"
|
|
name = "api:onboarding:users"
|
|
requires_auth = False
|
|
step = STEP_USER
|
|
|
|
@RequestDataValidator(
|
|
vol.Schema(
|
|
{
|
|
vol.Required("name"): str,
|
|
vol.Required("username"): str,
|
|
vol.Required("password"): str,
|
|
vol.Required("client_id"): str,
|
|
vol.Required("language"): str,
|
|
}
|
|
)
|
|
)
|
|
async def post(self, request, data):
|
|
"""Handle user creation, area creation."""
|
|
hass = request.app["hass"]
|
|
|
|
async with self._lock:
|
|
if self._async_is_done():
|
|
return self.json_message("User step already done", HTTP_FORBIDDEN)
|
|
|
|
provider = _async_get_hass_provider(hass)
|
|
await provider.async_initialize()
|
|
|
|
user = await hass.auth.async_create_user(data["name"], [GROUP_ID_ADMIN])
|
|
await hass.async_add_executor_job(
|
|
provider.data.add_auth, data["username"], data["password"]
|
|
)
|
|
credentials = await provider.async_get_or_create_credentials(
|
|
{"username": data["username"]}
|
|
)
|
|
await provider.data.async_save()
|
|
await hass.auth.async_link_user(user, credentials)
|
|
if "person" in hass.config.components:
|
|
await hass.components.person.async_create_person(
|
|
data["name"], user_id=user.id
|
|
)
|
|
|
|
# Create default areas using the users supplied language.
|
|
translations = await hass.helpers.translation.async_get_translations(
|
|
data["language"], "area", DOMAIN
|
|
)
|
|
|
|
area_registry = await hass.helpers.area_registry.async_get_registry()
|
|
|
|
for area in DEFAULT_AREAS:
|
|
area_registry.async_create(
|
|
translations[f"component.onboarding.area.{area}"]
|
|
)
|
|
|
|
await self._async_mark_done(hass)
|
|
|
|
# Return authorization code for fetching tokens and connect
|
|
# during onboarding.
|
|
auth_code = hass.components.auth.create_auth_code(data["client_id"], user)
|
|
return self.json({"auth_code": auth_code})
|
|
|
|
|
|
class CoreConfigOnboardingView(_BaseOnboardingView):
|
|
"""View to finish core config onboarding step."""
|
|
|
|
url = "/api/onboarding/core_config"
|
|
name = "api:onboarding:core_config"
|
|
step = STEP_CORE_CONFIG
|
|
|
|
async def post(self, request):
|
|
"""Handle finishing core config step."""
|
|
hass = request.app["hass"]
|
|
|
|
async with self._lock:
|
|
if self._async_is_done():
|
|
return self.json_message(
|
|
"Core config step already done", HTTP_FORBIDDEN
|
|
)
|
|
|
|
await self._async_mark_done(hass)
|
|
|
|
await hass.config_entries.flow.async_init(
|
|
"met", context={"source": "onboarding"}
|
|
)
|
|
|
|
return self.json({})
|
|
|
|
|
|
class IntegrationOnboardingView(_BaseOnboardingView):
|
|
"""View to finish integration onboarding step."""
|
|
|
|
url = "/api/onboarding/integration"
|
|
name = "api:onboarding:integration"
|
|
step = STEP_INTEGRATION
|
|
|
|
@RequestDataValidator(vol.Schema({vol.Required("client_id"): str}))
|
|
async def post(self, request, data):
|
|
"""Handle token creation."""
|
|
hass = request.app["hass"]
|
|
user = request["hass_user"]
|
|
|
|
async with self._lock:
|
|
if self._async_is_done():
|
|
return self.json_message(
|
|
"Integration step already done", HTTP_FORBIDDEN
|
|
)
|
|
|
|
await self._async_mark_done(hass)
|
|
|
|
# Return authorization code so we can redirect user and log them in
|
|
auth_code = hass.components.auth.create_auth_code(data["client_id"], user)
|
|
return self.json({"auth_code": auth_code})
|
|
|
|
|
|
@callback
|
|
def _async_get_hass_provider(hass):
|
|
"""Get the Home Assistant auth provider."""
|
|
for prv in hass.auth.auth_providers:
|
|
if prv.type == "homeassistant":
|
|
return prv
|
|
|
|
raise RuntimeError("No Home Assistant provider found")
|