1 """Account linking via the cloud."""
3 from __future__
import annotations
5 from datetime
import datetime
10 from awesomeversion
import AwesomeVersion
11 from hass_nabucasa
import account_link
17 from .const
import DATA_CLOUD, DOMAIN
19 DATA_SERVICES =
"cloud_account_link_services"
21 _LOGGER = logging.getLogger(__name__)
23 CURRENT_VERSION = AwesomeVersion(HA_VERSION)
24 CURRENT_PLAIN_VERSION = AwesomeVersion(
25 CURRENT_VERSION.string.removesuffix(f
"{CURRENT_VERSION.modifier}")
31 """Set up cloud account link."""
32 config_entry_oauth2_flow.async_add_implementation_provider(
33 hass, DOMAIN, async_provide_implementation
38 hass: HomeAssistant, domain: str
39 ) -> list[config_entry_oauth2_flow.AbstractOAuth2Implementation]:
40 """Provide an implementation for a domain."""
43 for service
in services:
45 service[
"service"] == domain
46 and service[
"min_version"] <= CURRENT_PLAIN_VERSION
48 service.get(
"accepts_new_authorizations",
True)
50 (entries := hass.config_entries.async_entries(domain))
52 entry.data.get(
"auth_implementation") == DOMAIN
64 """Get the available services."""
65 services: list[dict[str, Any]]
66 if DATA_SERVICES
in hass.data:
67 services = hass.data[DATA_SERVICES]
71 services = await account_link.async_fetch_available_services(
74 except (aiohttp.ClientError, TimeoutError):
77 hass.data[DATA_SERVICES] = services
80 def clear_services(_now: datetime) ->
None:
81 """Clear services cache."""
82 hass.data.pop(DATA_SERVICES,
None)
84 event.async_call_later(hass, CACHE_TIMEOUT, clear_services)
90 """Cloud implementation of the OAuth2 flow."""
92 def __init__(self, hass: HomeAssistant, service: str) ->
None:
93 """Initialize cloud OAuth2 implementation."""
99 """Name of the implementation."""
100 return "Home Assistant Cloud"
104 """Domain that is providing the implementation."""
108 """Generate a url for the user to authorize."""
109 helper = account_link.AuthorizeAccountHelper(
110 self.
hasshass.data[DATA_CLOUD], self.
serviceservice
112 authorize_url = await helper.async_get_authorize_url()
114 async
def await_tokens() -> None:
115 """Wait for tokens and pass them on when received."""
117 tokens = await helper.async_get_tokens()
120 _LOGGER.info(
"Timeout fetching tokens for flow %s", flow_id)
121 except account_link.AccountLinkException
as err:
123 "Failed to fetch tokens for flow %s: %s", flow_id, err.code
126 await self.
hasshass.config_entries.flow.async_configure(
127 flow_id=flow_id, user_input=tokens
130 self.
hasshass.async_create_task(await_tokens())
135 """Resolve external data to tokens."""
137 dict_data: dict = external_data
141 """Refresh a token."""
142 new_token = await account_link.async_fetch_access_token(
143 self.
hasshass.data[DATA_CLOUD], self.
serviceservice, token[
"refresh_token"]
145 return {**token, **new_token}
str async_generate_authorize_url(self, str flow_id)
dict async_resolve_external_data(self, Any external_data)
None __init__(self, HomeAssistant hass, str service)
dict _async_refresh_token(self, dict token)
list[config_entry_oauth2_flow.AbstractOAuth2Implementation] async_provide_implementation(HomeAssistant hass, str domain)
None async_setup(HomeAssistant hass)
list[dict[str, Any]] _get_services(HomeAssistant hass)