Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """Support to help onboard new users."""
2 
3 from __future__ import annotations
4 
5 from collections.abc import Callable
6 from dataclasses import dataclass
7 from typing import TYPE_CHECKING, TypedDict
8 
9 from homeassistant.core import HomeAssistant, callback
10 from homeassistant.helpers import config_validation as cv
11 from homeassistant.helpers.storage import Store
12 from homeassistant.helpers.typing import ConfigType
13 from homeassistant.loader import bind_hass
14 
15 from . import views
16 from .const import (
17  DOMAIN,
18  STEP_ANALYTICS,
19  STEP_CORE_CONFIG,
20  STEP_INTEGRATION,
21  STEP_USER,
22  STEPS,
23 )
24 
25 STORAGE_KEY = DOMAIN
26 STORAGE_VERSION = 4
27 
28 CONFIG_SCHEMA = cv.empty_config_schema(DOMAIN)
29 
30 
31 @dataclass
33  """Container for onboarding data."""
34 
35  listeners: list[Callable[[], None]]
36  onboarded: bool
37  steps: OnboardingStoreData
38 
39 
40 class OnboardingStoreData(TypedDict):
41  """Onboarding store data."""
42 
43  done: list[str]
44 
45 
46 class OnboardingStorage(Store[OnboardingStoreData]):
47  """Store onboarding data."""
48 
50  self,
51  old_major_version: int,
52  old_minor_version: int,
53  old_data: OnboardingStoreData,
54  ) -> OnboardingStoreData:
55  """Migrate to the new version."""
56  # From version 1 -> 2, we automatically mark the integration step done
57  if old_major_version < 2:
58  old_data["done"].append(STEP_INTEGRATION)
59  if old_major_version < 3:
60  old_data["done"].append(STEP_CORE_CONFIG)
61  if old_major_version < 4:
62  old_data["done"].append(STEP_ANALYTICS)
63  return old_data
64 
65 
66 @bind_hass
67 @callback
68 def async_is_onboarded(hass: HomeAssistant) -> bool:
69  """Return if Home Assistant has been onboarded."""
70  data: OnboardingData | None = hass.data.get(DOMAIN)
71  return data is None or data.onboarded is True
72 
73 
74 @bind_hass
75 @callback
76 def async_is_user_onboarded(hass: HomeAssistant) -> bool:
77  """Return if a user has been created as part of onboarding."""
78  return async_is_onboarded(hass) or STEP_USER in hass.data[DOMAIN].steps["done"]
79 
80 
81 @callback
82 def async_add_listener(hass: HomeAssistant, listener: Callable[[], None]) -> None:
83  """Add a listener to be called when onboarding is complete."""
84  data: OnboardingData | None = hass.data.get(DOMAIN)
85 
86  if not data:
87  # Onboarding not active
88  return
89 
90  if data.onboarded:
91  listener()
92  return
93 
94  data.listeners.append(listener)
95 
96 
97 async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
98  """Set up the onboarding component."""
99  store = OnboardingStorage(hass, STORAGE_VERSION, STORAGE_KEY, private=True)
100  data: OnboardingStoreData | None
101  if (data := await store.async_load()) is None:
102  data = {"done": []}
103 
104  if TYPE_CHECKING:
105  assert isinstance(data, dict)
106 
107  if STEP_USER not in data["done"]:
108  # Users can already have created an owner account via the command line
109  # If so, mark the user step as done.
110  has_owner = False
111 
112  for user in await hass.auth.async_get_users():
113  if user.is_owner:
114  has_owner = True
115  break
116 
117  if has_owner:
118  data["done"].append(STEP_USER)
119  await store.async_save(data)
120 
121  if set(data["done"]) == set(STEPS):
122  return True
123 
124  hass.data[DOMAIN] = OnboardingData([], False, data)
125 
126  await views.async_setup(hass, data, store)
127 
128  return True
OnboardingStoreData _async_migrate_func(self, int old_major_version, int old_minor_version, OnboardingStoreData old_data)
Definition: __init__.py:54
None async_add_listener(HomeAssistant hass, Callable[[], None] listener)
Definition: __init__.py:82
bool async_setup(HomeAssistant hass, ConfigType config)
Definition: __init__.py:97
bool async_is_user_onboarded(HomeAssistant hass)
Definition: __init__.py:76
bool async_is_onboarded(HomeAssistant hass)
Definition: __init__.py:68