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:
Allen Porter 2020-10-21 01:17:49 -07:00 committed by GitHub
parent 9bc0509f28
commit 52b66e88c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1001 additions and 230 deletions

View file

@ -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")