Update Nest integration to support Google Nest Device Access (new API) (#41689)
Co-authored-by: Paulus Schoutsen <paulus@home-assistant.io>
This commit is contained in:
parent
9bc0509f28
commit
52b66e88c7
17 changed files with 1001 additions and 230 deletions
|
@ -1,8 +1,22 @@
|
|||
"""Config flow to configure Nest."""
|
||||
"""Config flow to configure Nest.
|
||||
|
||||
This configuration flow supports two APIs:
|
||||
- The new Device Access program and the Smart Device Management API
|
||||
- The legacy nest API
|
||||
|
||||
NestFlowHandler is an implementation of AbstractOAuth2FlowHandler with
|
||||
some overrides to support the old APIs auth flow. That is, for the new
|
||||
API this class has hardly any special config other than url parameters,
|
||||
and everything else custom is for the old api. When configured with the
|
||||
new api via NestFlowHandler.register_sdm_api, the custom methods just
|
||||
invoke the AbstractOAuth2FlowHandler methods.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from collections import OrderedDict
|
||||
import logging
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
import async_timeout
|
||||
import voluptuous as vol
|
||||
|
@ -10,9 +24,10 @@ import voluptuous as vol
|
|||
from homeassistant import config_entries
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers import config_entry_oauth2_flow
|
||||
from homeassistant.util.json import load_json
|
||||
|
||||
from .const import DOMAIN
|
||||
from .const import DATA_SDM, DOMAIN, SDM_SCOPES
|
||||
|
||||
DATA_FLOW_IMPL = "nest_flow_implementation"
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -20,7 +35,7 @@ _LOGGER = logging.getLogger(__name__)
|
|||
|
||||
@callback
|
||||
def register_flow_implementation(hass, domain, name, gen_authorize_url, convert_code):
|
||||
"""Register a flow implementation.
|
||||
"""Register a flow implementation for legacy api.
|
||||
|
||||
domain: Domain of the component responsible for the implementation.
|
||||
name: Name of the component.
|
||||
|
@ -47,22 +62,57 @@ class CodeInvalid(NestAuthError):
|
|||
|
||||
|
||||
@config_entries.HANDLERS.register(DOMAIN)
|
||||
class NestFlowHandler(config_entries.ConfigFlow):
|
||||
"""Handle a Nest config flow."""
|
||||
class NestFlowHandler(
|
||||
config_entry_oauth2_flow.AbstractOAuth2FlowHandler, domain=DOMAIN
|
||||
):
|
||||
"""Config flow to handle authentication for both APIs."""
|
||||
|
||||
DOMAIN = DOMAIN
|
||||
VERSION = 1
|
||||
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_PUSH
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the Nest config flow."""
|
||||
self.flow_impl = None
|
||||
@classmethod
|
||||
def register_sdm_api(cls, hass):
|
||||
"""Configure the flow handler to use the SDM API."""
|
||||
if DOMAIN not in hass.data:
|
||||
hass.data[DOMAIN] = {}
|
||||
hass.data[DOMAIN][DATA_SDM] = {}
|
||||
|
||||
def is_sdm_api(self):
|
||||
"""Return true if this flow is setup to use SDM API."""
|
||||
return DOMAIN in self.hass.data and DATA_SDM in self.hass.data[DOMAIN]
|
||||
|
||||
@property
|
||||
def logger(self) -> logging.Logger:
|
||||
"""Return logger."""
|
||||
return logging.getLogger(__name__)
|
||||
|
||||
@property
|
||||
def extra_authorize_data(self) -> Dict[str, str]:
|
||||
"""Extra data that needs to be appended to the authorize url."""
|
||||
return {
|
||||
"scope": " ".join(SDM_SCOPES),
|
||||
# Add params to ensure we get back a refresh token
|
||||
"access_type": "offline",
|
||||
"prompt": "consent",
|
||||
}
|
||||
|
||||
async def async_oauth_create_entry(self, data: dict) -> dict:
|
||||
"""Create an entry for the SDM flow."""
|
||||
data[DATA_SDM] = {}
|
||||
return await super().async_oauth_create_entry(data)
|
||||
|
||||
async def async_step_user(self, user_input=None):
|
||||
"""Handle a flow initialized by the user."""
|
||||
if self.is_sdm_api():
|
||||
return await super().async_step_user(user_input)
|
||||
return await self.async_step_init(user_input)
|
||||
|
||||
async def async_step_init(self, user_input=None):
|
||||
"""Handle a flow start."""
|
||||
if self.is_sdm_api():
|
||||
return None
|
||||
|
||||
flows = self.hass.data.get(DATA_FLOW_IMPL, {})
|
||||
|
||||
if self.hass.config_entries.async_entries(DOMAIN):
|
||||
|
@ -91,6 +141,9 @@ class NestFlowHandler(config_entries.ConfigFlow):
|
|||
implementation type we expect a pin or an external component to
|
||||
deliver the authentication code.
|
||||
"""
|
||||
if self.is_sdm_api():
|
||||
return None
|
||||
|
||||
flow = self.hass.data[DATA_FLOW_IMPL][self.flow_impl]
|
||||
|
||||
errors = {}
|
||||
|
@ -131,6 +184,9 @@ class NestFlowHandler(config_entries.ConfigFlow):
|
|||
|
||||
async def async_step_import(self, info):
|
||||
"""Import existing auth from Nest."""
|
||||
if self.is_sdm_api():
|
||||
return None
|
||||
|
||||
if self.hass.config_entries.async_entries(DOMAIN):
|
||||
return self.async_abort(reason="single_instance_allowed")
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue