diff --git a/CODEOWNERS b/CODEOWNERS
index 91ae8131db8b3c46b0b26bf08b1dea272e4a0765..17138e937699277d6328102cbf2fbee4d9a1fa6d 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -278,7 +278,7 @@ homeassistant/components/neato/* @dshokouhi @Santobert
 homeassistant/components/nederlandse_spoorwegen/* @YarmoM
 homeassistant/components/nello/* @pschmitt
 homeassistant/components/ness_alarm/* @nickw444
-homeassistant/components/nest/* @awarecan
+homeassistant/components/nest/* @awarecan @allenporter
 homeassistant/components/netatmo/* @cgtobi
 homeassistant/components/netdata/* @fabaff
 homeassistant/components/nexia/* @ryannazaretian @bdraco
diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py
index 84390a1a4c33d7b07712d770a0df99f2b251dad7..ae32527bdc4d03176c626416e73e6e4a6f1ebf6b 100644
--- a/homeassistant/components/nest/__init__.py
+++ b/homeassistant/components/nest/__init__.py
@@ -1,13 +1,18 @@
 """Support for Nest devices."""
+
+import asyncio
 from datetime import datetime, timedelta
 import logging
 import threading
 
+from google_nest_sdm.event import EventCallback, EventMessage
+from google_nest_sdm.google_nest_subscriber import GoogleNestSubscriber
 from nest import Nest
 from nest.nest import APIError, AuthorizationError
 import voluptuous as vol
 
 from homeassistant import config_entries
+from homeassistant.config_entries import ConfigEntry
 from homeassistant.const import (
     CONF_BINARY_SENSORS,
     CONF_CLIENT_ID,
@@ -19,25 +24,38 @@ from homeassistant.const import (
     EVENT_HOMEASSISTANT_START,
     EVENT_HOMEASSISTANT_STOP,
 )
-from homeassistant.core import callback
-from homeassistant.helpers import config_validation as cv
+from homeassistant.core import HomeAssistant, callback
+from homeassistant.helpers import (
+    aiohttp_client,
+    config_entry_oauth2_flow,
+    config_validation as cv,
+)
 from homeassistant.helpers.dispatcher import async_dispatcher_connect, dispatcher_send
 from homeassistant.helpers.entity import Entity
 
-from . import local_auth
-from .const import DOMAIN
+from . import api, config_flow, local_auth
+from .const import (
+    API_URL,
+    DATA_SDM,
+    DOMAIN,
+    OAUTH2_AUTHORIZE,
+    OAUTH2_TOKEN,
+    SIGNAL_NEST_UPDATE,
+)
 
 _CONFIGURING = {}
 _LOGGER = logging.getLogger(__name__)
 
+CONF_PROJECT_ID = "project_id"
+CONF_SUBSCRIBER_ID = "subscriber_id"
+
+# Configuration for the legacy nest API
 SERVICE_CANCEL_ETA = "cancel_eta"
 SERVICE_SET_ETA = "set_eta"
 
 DATA_NEST = "nest"
 DATA_NEST_CONFIG = "nest_config"
 
-SIGNAL_NEST_UPDATE = "nest_update"
-
 NEST_CONFIG_FILE = "nest.conf"
 
 ATTR_ETA = "eta"
@@ -61,6 +79,10 @@ CONFIG_SCHEMA = vol.Schema(
             {
                 vol.Required(CONF_CLIENT_ID): cv.string,
                 vol.Required(CONF_CLIENT_SECRET): cv.string,
+                # Required to use the new API (optional for compatibility)
+                vol.Optional(CONF_PROJECT_ID): cv.string,
+                vol.Optional(CONF_SUBSCRIBER_ID): cv.string,
+                # Config that only currently works on the old API
                 vol.Optional(CONF_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
                 vol.Optional(CONF_SENSORS): SENSOR_SCHEMA,
                 vol.Optional(CONF_BINARY_SENSORS): SENSOR_SCHEMA,
@@ -70,6 +92,10 @@ CONFIG_SCHEMA = vol.Schema(
     extra=vol.ALLOW_EXTRA,
 )
 
+PLATFORMS = ["sensor"]
+
+# Services for the legacy API
+
 SET_AWAY_MODE_SCHEMA = vol.Schema(
     {
         vol.Required(ATTR_AWAY_MODE): vol.In([AWAY_MODE_AWAY, AWAY_MODE_HOME]),
@@ -94,10 +120,128 @@ CANCEL_ETA_SCHEMA = vol.Schema(
 )
 
 
+async def async_setup(hass: HomeAssistant, config: dict):
+    """Set up Nest components with dispatch between old/new flows."""
+    hass.data[DOMAIN] = {}
+
+    if DOMAIN not in config:
+        return True
+
+    if CONF_PROJECT_ID not in config[DOMAIN]:
+        return await async_setup_legacy(hass, config)
+
+    if CONF_SUBSCRIBER_ID not in config[DOMAIN]:
+        _LOGGER.error("Configuration option '{CONF_SUBSCRIBER_ID}' required")
+        return False
+
+    # For setup of ConfigEntry below
+    hass.data[DOMAIN][DATA_NEST_CONFIG] = config[DOMAIN]
+    project_id = config[DOMAIN][CONF_PROJECT_ID]
+    config_flow.NestFlowHandler.register_sdm_api(hass)
+    config_flow.NestFlowHandler.async_register_implementation(
+        hass,
+        config_entry_oauth2_flow.LocalOAuth2Implementation(
+            hass,
+            DOMAIN,
+            config[DOMAIN][CONF_CLIENT_ID],
+            config[DOMAIN][CONF_CLIENT_SECRET],
+            OAUTH2_AUTHORIZE.format(project_id=project_id),
+            OAUTH2_TOKEN,
+        ),
+    )
+
+    return True
+
+
+class SignalUpdateCallback(EventCallback):
+    """An EventCallback invoked when new events arrive from subscriber."""
+
+    def __init__(self, hass: HomeAssistant):
+        """Initialize EventCallback."""
+        self._hass = hass
+
+    def handle_event(self, event_message: EventMessage):
+        """Process an incoming EventMessage."""
+        _LOGGER.debug("Update %s @ %s", event_message.event_id, event_message.timestamp)
+        traits = event_message.resource_update_traits
+        if traits:
+            _LOGGER.debug("Trait update %s", traits.keys())
+        events = event_message.resource_update_events
+        if events:
+            _LOGGER.debug("Event Update %s", events.keys())
+
+        if not event_message.resource_update_traits:
+            # Note: Currently ignoring events like camera motion
+            return
+        # This event triggered an update to a device that changed some
+        # properties which the DeviceManager should already have received.
+        # Send a signal to refresh state of all listening devices.
+        dispatcher_send(self._hass, SIGNAL_NEST_UPDATE)
+
+
+async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry):
+    """Set up Nest from a config entry with dispatch between old/new flows."""
+
+    if DATA_SDM not in entry.data:
+        return await async_setup_legacy_entry(hass, entry)
+
+    implementation = (
+        await config_entry_oauth2_flow.async_get_config_entry_implementation(
+            hass, entry
+        )
+    )
+
+    config = hass.data[DOMAIN][DATA_NEST_CONFIG]
+
+    session = config_entry_oauth2_flow.OAuth2Session(hass, entry, implementation)
+    auth = api.AsyncConfigEntryAuth(
+        aiohttp_client.async_get_clientsession(hass),
+        session,
+        API_URL,
+    )
+    subscriber = GoogleNestSubscriber(
+        auth, config[CONF_PROJECT_ID], config[CONF_SUBSCRIBER_ID]
+    )
+    subscriber.set_update_callback(SignalUpdateCallback(hass))
+    hass.loop.create_task(subscriber.start_async())
+    hass.data[DOMAIN][entry.entry_id] = subscriber
+
+    for component in PLATFORMS:
+        hass.async_create_task(
+            hass.config_entries.async_forward_entry_setup(entry, component)
+        )
+
+    return True
+
+
+async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
+    """Unload a config entry."""
+    if DATA_SDM not in entry.data:
+        # Legacy API
+        return True
+
+    subscriber = hass.data[DOMAIN][entry.entry_id]
+    subscriber.stop_async()
+    unload_ok = all(
+        await asyncio.gather(
+            *[
+                hass.config_entries.async_forward_entry_unload(entry, component)
+                for component in PLATFORMS
+            ]
+        )
+    )
+    if unload_ok:
+        hass.data[DOMAIN].pop(entry.entry_id)
+
+    return unload_ok
+
+
 def nest_update_event_broker(hass, nest):
     """
     Dispatch SIGNAL_NEST_UPDATE to devices when nest stream API received data.
 
+    Used for the legacy nest API.
+
     Runs in its own thread.
     """
     _LOGGER.debug("Listening for nest.update_event")
@@ -115,8 +259,8 @@ def nest_update_event_broker(hass, nest):
     _LOGGER.debug("Stop listening for nest.update_event")
 
 
-async def async_setup(hass, config):
-    """Set up Nest components."""
+async def async_setup_legacy(hass, config):
+    """Set up Nest components using the legacy nest API."""
     if DOMAIN not in config:
         return True
 
@@ -141,14 +285,14 @@ async def async_setup(hass, config):
     return True
 
 
-async def async_setup_entry(hass, entry):
-    """Set up Nest from a config entry."""
+async def async_setup_legacy_entry(hass, entry):
+    """Set up Nest from legacy config entry."""
 
     nest = Nest(access_token=entry.data["tokens"]["access_token"])
 
     _LOGGER.debug("proceeding with setup")
     conf = hass.data.get(DATA_NEST_CONFIG, {})
-    hass.data[DATA_NEST] = NestDevice(hass, conf, nest)
+    hass.data[DATA_NEST] = NestLegacyDevice(hass, conf, nest)
     if not await hass.async_add_executor_job(hass.data[DATA_NEST].initialize):
         return False
 
@@ -275,8 +419,8 @@ async def async_setup_entry(hass, entry):
     return True
 
 
-class NestDevice:
-    """Structure Nest functions for hass."""
+class NestLegacyDevice:
+    """Structure Nest functions for hass for legacy API."""
 
     def __init__(self, hass, conf, nest):
         """Init Nest Devices."""
diff --git a/homeassistant/components/nest/api.py b/homeassistant/components/nest/api.py
new file mode 100644
index 0000000000000000000000000000000000000000..46138469d6d26574fc574cfc48e178724a5d4d55
--- /dev/null
+++ b/homeassistant/components/nest/api.py
@@ -0,0 +1,35 @@
+"""API for Google Nest Device Access bound to Home Assistant OAuth."""
+
+from aiohttp import ClientSession
+from google.oauth2.credentials import Credentials
+from google_nest_sdm.auth import AbstractAuth
+
+from homeassistant.helpers import config_entry_oauth2_flow
+
+# See https://developers.google.com/nest/device-access/registration
+
+
+class AsyncConfigEntryAuth(AbstractAuth):
+    """Provide Google Nest Device Access authentication tied to an OAuth2 based config entry."""
+
+    def __init__(
+        self,
+        websession: ClientSession,
+        oauth_session: config_entry_oauth2_flow.OAuth2Session,
+        api_url: str,
+    ):
+        """Initialize Google Nest Device Access auth."""
+        super().__init__(websession, api_url)
+        self._oauth_session = oauth_session
+
+    async def async_get_access_token(self):
+        """Return a valid access token."""
+        if not self._oauth_session.valid_token:
+            await self._oauth_session.async_ensure_token_valid()
+
+        return self._oauth_session.token["access_token"]
+
+    async def async_get_creds(self):
+        """Return a minimal OAuth credential."""
+        token = await self.async_get_access_token()
+        return Credentials(token=token)
diff --git a/homeassistant/components/nest/climate.py b/homeassistant/components/nest/climate.py
index 7fdc1f7ac121cbaa8e3bef32b8e03c84942f697d..7571845e66ea6f1981ec0553105dd6c2bee5d570 100644
--- a/homeassistant/components/nest/climate.py
+++ b/homeassistant/components/nest/climate.py
@@ -33,7 +33,8 @@ from homeassistant.const import (
 )
 from homeassistant.helpers.dispatcher import async_dispatcher_connect
 
-from . import DATA_NEST, DOMAIN as NEST_DOMAIN, SIGNAL_NEST_UPDATE
+from . import DATA_NEST, DOMAIN as NEST_DOMAIN
+from .const import SIGNAL_NEST_UPDATE
 
 _LOGGER = logging.getLogger(__name__)
 
diff --git a/homeassistant/components/nest/config_flow.py b/homeassistant/components/nest/config_flow.py
index 87612f300640bd8974500a8aee7be2189c0dd47a..aa4b7af7e12c226093388aee1cdcb82921796fba 100644
--- a/homeassistant/components/nest/config_flow.py
+++ b/homeassistant/components/nest/config_flow.py
@@ -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")
 
diff --git a/homeassistant/components/nest/const.py b/homeassistant/components/nest/const.py
index fab77c6b5a68c3b94c86476c441f59e96f5a8f13..b418df97bba82b273730a21bd8de3e1bed10c7ca 100644
--- a/homeassistant/components/nest/const.py
+++ b/homeassistant/components/nest/const.py
@@ -1,2 +1,17 @@
 """Constants used by the Nest component."""
+
 DOMAIN = "nest"
+DATA_SDM = "sdm"
+
+SIGNAL_NEST_UPDATE = "nest_update"
+
+# For the Google Nest Device Access API
+OAUTH2_AUTHORIZE = (
+    "https://nestservices.google.com/partnerconnections/{project_id}/auth"
+)
+OAUTH2_TOKEN = "https://www.googleapis.com/oauth2/v4/token"
+SDM_SCOPES = [
+    "https://www.googleapis.com/auth/sdm.service",
+    "https://www.googleapis.com/auth/pubsub",
+]
+API_URL = "https://smartdevicemanagement.googleapis.com/v1"
diff --git a/homeassistant/components/nest/local_auth.py b/homeassistant/components/nest/local_auth.py
index f54da8039ffe7de1b88d692a973687e99eac1f46..8be2693325e030e3be5c5c5d60ccbb71f4478b03 100644
--- a/homeassistant/components/nest/local_auth.py
+++ b/homeassistant/components/nest/local_auth.py
@@ -1,4 +1,4 @@
-"""Local Nest authentication."""
+"""Local Nest authentication for the legacy api."""
 import asyncio
 from functools import partial
 
diff --git a/homeassistant/components/nest/manifest.json b/homeassistant/components/nest/manifest.json
index 610e80d9a6a1c0ce04d6a8696e7cd38ce7297f9c..0686b5add1dcb8f9bf413e6f5f4060437f8b3a81 100644
--- a/homeassistant/components/nest/manifest.json
+++ b/homeassistant/components/nest/manifest.json
@@ -2,7 +2,14 @@
   "domain": "nest",
   "name": "Nest",
   "config_flow": true,
+  "dependencies": ["http"],
   "documentation": "https://www.home-assistant.io/integrations/nest",
-  "requirements": ["python-nest==4.1.0"],
-  "codeowners": ["@awarecan"]
+  "requirements": [
+      "python-nest==4.1.0",
+      "google-nest-sdm==0.1.6"
+  ],
+  "codeowners": [
+      "@awarecan",
+      "@allenporter"
+  ]
 }
diff --git a/homeassistant/components/nest/sensor.py b/homeassistant/components/nest/sensor.py
index 8c4418e7536d93e44abc7001c773598fed1c9919..b3df3dd393e6741031d169e57c7d12595d4b66da 100644
--- a/homeassistant/components/nest/sensor.py
+++ b/homeassistant/components/nest/sensor.py
@@ -1,208 +1,17 @@
-"""Support for Nest Thermostat sensors."""
-import logging
+"""Support for Nest sensors that dispatches between API versions."""
 
-from homeassistant.const import (
-    CONF_MONITORED_CONDITIONS,
-    DEVICE_CLASS_HUMIDITY,
-    DEVICE_CLASS_TEMPERATURE,
-    PERCENTAGE,
-    STATE_OFF,
-    TEMP_CELSIUS,
-    TEMP_FAHRENHEIT,
-)
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.helpers.typing import HomeAssistantType
 
-from . import CONF_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice
+from .const import DATA_SDM
+from .sensor_legacy import async_setup_legacy_entry
+from .sensor_sdm import async_setup_sdm_entry
 
-SENSOR_TYPES = ["humidity", "operation_mode", "hvac_state"]
 
-TEMP_SENSOR_TYPES = ["temperature", "target"]
-
-PROTECT_SENSOR_TYPES = [
-    "co_status",
-    "smoke_status",
-    "battery_health",
-    # color_status: "gray", "green", "yellow", "red"
-    "color_status",
-]
-
-STRUCTURE_SENSOR_TYPES = ["eta"]
-
-STATE_HEAT = "heat"
-STATE_COOL = "cool"
-
-# security_state is structure level sensor, but only meaningful when
-# Nest Cam exist
-STRUCTURE_CAMERA_SENSOR_TYPES = ["security_state"]
-
-_VALID_SENSOR_TYPES = (
-    SENSOR_TYPES
-    + TEMP_SENSOR_TYPES
-    + PROTECT_SENSOR_TYPES
-    + STRUCTURE_SENSOR_TYPES
-    + STRUCTURE_CAMERA_SENSOR_TYPES
-)
-
-SENSOR_UNITS = {"humidity": PERCENTAGE}
-
-SENSOR_DEVICE_CLASSES = {"humidity": DEVICE_CLASS_HUMIDITY}
-
-VARIABLE_NAME_MAPPING = {"eta": "eta_begin", "operation_mode": "mode"}
-
-VALUE_MAPPING = {
-    "hvac_state": {"heating": STATE_HEAT, "cooling": STATE_COOL, "off": STATE_OFF}
-}
-
-SENSOR_TYPES_DEPRECATED = ["last_ip", "local_ip", "last_connection", "battery_level"]
-
-DEPRECATED_WEATHER_VARS = [
-    "weather_humidity",
-    "weather_temperature",
-    "weather_condition",
-    "wind_speed",
-    "wind_direction",
-]
-
-_SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED + DEPRECATED_WEATHER_VARS
-
-_LOGGER = logging.getLogger(__name__)
-
-
-def setup_platform(hass, config, add_entities, discovery_info=None):
-    """Set up the Nest Sensor.
-
-    No longer used.
-    """
-
-
-async def async_setup_entry(hass, entry, async_add_entities):
-    """Set up a Nest sensor based on a config entry."""
-    nest = hass.data[DATA_NEST]
-
-    discovery_info = hass.data.get(DATA_NEST_CONFIG, {}).get(CONF_SENSORS, {})
-
-    # Add all available sensors if no Nest sensor config is set
-    if discovery_info == {}:
-        conditions = _VALID_SENSOR_TYPES
-    else:
-        conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {})
-
-    for variable in conditions:
-        if variable in _SENSOR_TYPES_DEPRECATED:
-            if variable in DEPRECATED_WEATHER_VARS:
-                wstr = (
-                    "Nest no longer provides weather data like %s. See "
-                    "https://www.home-assistant.io/integrations/#weather "
-                    "for a list of other weather integrations to use." % variable
-                )
-            else:
-                wstr = (
-                    f"{variable} is no a longer supported "
-                    "monitored_conditions. See "
-                    "https://www.home-assistant.io/integrations/"
-                    "binary_sensor.nest/ for valid options."
-                )
-            _LOGGER.error(wstr)
-
-    def get_sensors():
-        """Get the Nest sensors."""
-        all_sensors = []
-        for structure in nest.structures():
-            all_sensors += [
-                NestBasicSensor(structure, None, variable)
-                for variable in conditions
-                if variable in STRUCTURE_SENSOR_TYPES
-            ]
-
-        for structure, device in nest.thermostats():
-            all_sensors += [
-                NestBasicSensor(structure, device, variable)
-                for variable in conditions
-                if variable in SENSOR_TYPES
-            ]
-            all_sensors += [
-                NestTempSensor(structure, device, variable)
-                for variable in conditions
-                if variable in TEMP_SENSOR_TYPES
-            ]
-
-        for structure, device in nest.smoke_co_alarms():
-            all_sensors += [
-                NestBasicSensor(structure, device, variable)
-                for variable in conditions
-                if variable in PROTECT_SENSOR_TYPES
-            ]
-
-        structures_has_camera = {}
-        for structure, device in nest.cameras():
-            structures_has_camera[structure] = True
-        for structure in structures_has_camera:
-            all_sensors += [
-                NestBasicSensor(structure, None, variable)
-                for variable in conditions
-                if variable in STRUCTURE_CAMERA_SENSOR_TYPES
-            ]
-
-        return all_sensors
-
-    async_add_entities(await hass.async_add_executor_job(get_sensors), True)
-
-
-class NestBasicSensor(NestSensorDevice):
-    """Representation a basic Nest sensor."""
-
-    @property
-    def state(self):
-        """Return the state of the sensor."""
-        return self._state
-
-    @property
-    def device_class(self):
-        """Return the device class of the sensor."""
-        return SENSOR_DEVICE_CLASSES.get(self.variable)
-
-    def update(self):
-        """Retrieve latest state."""
-        self._unit = SENSOR_UNITS.get(self.variable)
-
-        if self.variable in VARIABLE_NAME_MAPPING:
-            self._state = getattr(self.device, VARIABLE_NAME_MAPPING[self.variable])
-        elif self.variable in VALUE_MAPPING:
-            state = getattr(self.device, self.variable)
-            self._state = VALUE_MAPPING[self.variable].get(state, state)
-        elif self.variable in PROTECT_SENSOR_TYPES and self.variable != "color_status":
-            # keep backward compatibility
-            state = getattr(self.device, self.variable)
-            self._state = state.capitalize() if state is not None else None
-        else:
-            self._state = getattr(self.device, self.variable)
-
-
-class NestTempSensor(NestSensorDevice):
-    """Representation of a Nest Temperature sensor."""
-
-    @property
-    def state(self):
-        """Return the state of the sensor."""
-        return self._state
-
-    @property
-    def device_class(self):
-        """Return the device class of the sensor."""
-        return DEVICE_CLASS_TEMPERATURE
-
-    def update(self):
-        """Retrieve latest state."""
-        if self.device.temperature_scale == "C":
-            self._unit = TEMP_CELSIUS
-        else:
-            self._unit = TEMP_FAHRENHEIT
-
-        temp = getattr(self.device, self.variable)
-        if temp is None:
-            self._state = None
-
-        if isinstance(temp, tuple):
-            low, high = temp
-            self._state = f"{int(low)}-{int(high)}"
-        else:
-            self._state = round(temp, 1)
+async def async_setup_entry(
+    hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
+) -> None:
+    """Set up the sensors."""
+    if DATA_SDM not in entry.data:
+        return await async_setup_legacy_entry(hass, entry, async_add_entities)
+    return await async_setup_sdm_entry(hass, entry, async_add_entities)
diff --git a/homeassistant/components/nest/sensor_legacy.py b/homeassistant/components/nest/sensor_legacy.py
new file mode 100644
index 0000000000000000000000000000000000000000..2df668513e16146bf6b6c5e5bc27d159295f1e0b
--- /dev/null
+++ b/homeassistant/components/nest/sensor_legacy.py
@@ -0,0 +1,208 @@
+"""Support for Nest Thermostat sensors for the legacy API."""
+import logging
+
+from homeassistant.const import (
+    CONF_MONITORED_CONDITIONS,
+    DEVICE_CLASS_HUMIDITY,
+    DEVICE_CLASS_TEMPERATURE,
+    PERCENTAGE,
+    STATE_OFF,
+    TEMP_CELSIUS,
+    TEMP_FAHRENHEIT,
+)
+
+from . import CONF_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice
+
+SENSOR_TYPES = ["humidity", "operation_mode", "hvac_state"]
+
+TEMP_SENSOR_TYPES = ["temperature", "target"]
+
+PROTECT_SENSOR_TYPES = [
+    "co_status",
+    "smoke_status",
+    "battery_health",
+    # color_status: "gray", "green", "yellow", "red"
+    "color_status",
+]
+
+STRUCTURE_SENSOR_TYPES = ["eta"]
+
+STATE_HEAT = "heat"
+STATE_COOL = "cool"
+
+# security_state is structure level sensor, but only meaningful when
+# Nest Cam exist
+STRUCTURE_CAMERA_SENSOR_TYPES = ["security_state"]
+
+_VALID_SENSOR_TYPES = (
+    SENSOR_TYPES
+    + TEMP_SENSOR_TYPES
+    + PROTECT_SENSOR_TYPES
+    + STRUCTURE_SENSOR_TYPES
+    + STRUCTURE_CAMERA_SENSOR_TYPES
+)
+
+SENSOR_UNITS = {"humidity": PERCENTAGE}
+
+SENSOR_DEVICE_CLASSES = {"humidity": DEVICE_CLASS_HUMIDITY}
+
+VARIABLE_NAME_MAPPING = {"eta": "eta_begin", "operation_mode": "mode"}
+
+VALUE_MAPPING = {
+    "hvac_state": {"heating": STATE_HEAT, "cooling": STATE_COOL, "off": STATE_OFF}
+}
+
+SENSOR_TYPES_DEPRECATED = ["last_ip", "local_ip", "last_connection", "battery_level"]
+
+DEPRECATED_WEATHER_VARS = [
+    "weather_humidity",
+    "weather_temperature",
+    "weather_condition",
+    "wind_speed",
+    "wind_direction",
+]
+
+_SENSOR_TYPES_DEPRECATED = SENSOR_TYPES_DEPRECATED + DEPRECATED_WEATHER_VARS
+
+_LOGGER = logging.getLogger(__name__)
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up the Nest Sensor.
+
+    No longer used.
+    """
+
+
+async def async_setup_legacy_entry(hass, entry, async_add_entities):
+    """Set up a Nest sensor based on a config entry."""
+    nest = hass.data[DATA_NEST]
+
+    discovery_info = hass.data.get(DATA_NEST_CONFIG, {}).get(CONF_SENSORS, {})
+
+    # Add all available sensors if no Nest sensor config is set
+    if discovery_info == {}:
+        conditions = _VALID_SENSOR_TYPES
+    else:
+        conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {})
+
+    for variable in conditions:
+        if variable in _SENSOR_TYPES_DEPRECATED:
+            if variable in DEPRECATED_WEATHER_VARS:
+                wstr = (
+                    "Nest no longer provides weather data like %s. See "
+                    "https://www.home-assistant.io/integrations/#weather "
+                    "for a list of other weather integrations to use." % variable
+                )
+            else:
+                wstr = (
+                    f"{variable} is no a longer supported "
+                    "monitored_conditions. See "
+                    "https://www.home-assistant.io/integrations/"
+                    "binary_sensor.nest/ for valid options."
+                )
+            _LOGGER.error(wstr)
+
+    def get_sensors():
+        """Get the Nest sensors."""
+        all_sensors = []
+        for structure in nest.structures():
+            all_sensors += [
+                NestBasicSensor(structure, None, variable)
+                for variable in conditions
+                if variable in STRUCTURE_SENSOR_TYPES
+            ]
+
+        for structure, device in nest.thermostats():
+            all_sensors += [
+                NestBasicSensor(structure, device, variable)
+                for variable in conditions
+                if variable in SENSOR_TYPES
+            ]
+            all_sensors += [
+                NestTempSensor(structure, device, variable)
+                for variable in conditions
+                if variable in TEMP_SENSOR_TYPES
+            ]
+
+        for structure, device in nest.smoke_co_alarms():
+            all_sensors += [
+                NestBasicSensor(structure, device, variable)
+                for variable in conditions
+                if variable in PROTECT_SENSOR_TYPES
+            ]
+
+        structures_has_camera = {}
+        for structure, device in nest.cameras():
+            structures_has_camera[structure] = True
+        for structure in structures_has_camera:
+            all_sensors += [
+                NestBasicSensor(structure, None, variable)
+                for variable in conditions
+                if variable in STRUCTURE_CAMERA_SENSOR_TYPES
+            ]
+
+        return all_sensors
+
+    async_add_entities(await hass.async_add_executor_job(get_sensors), True)
+
+
+class NestBasicSensor(NestSensorDevice):
+    """Representation a basic Nest sensor."""
+
+    @property
+    def state(self):
+        """Return the state of the sensor."""
+        return self._state
+
+    @property
+    def device_class(self):
+        """Return the device class of the sensor."""
+        return SENSOR_DEVICE_CLASSES.get(self.variable)
+
+    def update(self):
+        """Retrieve latest state."""
+        self._unit = SENSOR_UNITS.get(self.variable)
+
+        if self.variable in VARIABLE_NAME_MAPPING:
+            self._state = getattr(self.device, VARIABLE_NAME_MAPPING[self.variable])
+        elif self.variable in VALUE_MAPPING:
+            state = getattr(self.device, self.variable)
+            self._state = VALUE_MAPPING[self.variable].get(state, state)
+        elif self.variable in PROTECT_SENSOR_TYPES and self.variable != "color_status":
+            # keep backward compatibility
+            state = getattr(self.device, self.variable)
+            self._state = state.capitalize() if state is not None else None
+        else:
+            self._state = getattr(self.device, self.variable)
+
+
+class NestTempSensor(NestSensorDevice):
+    """Representation of a Nest Temperature sensor."""
+
+    @property
+    def state(self):
+        """Return the state of the sensor."""
+        return self._state
+
+    @property
+    def device_class(self):
+        """Return the device class of the sensor."""
+        return DEVICE_CLASS_TEMPERATURE
+
+    def update(self):
+        """Retrieve latest state."""
+        if self.device.temperature_scale == "C":
+            self._unit = TEMP_CELSIUS
+        else:
+            self._unit = TEMP_FAHRENHEIT
+
+        temp = getattr(self.device, self.variable)
+        if temp is None:
+            self._state = None
+
+        if isinstance(temp, tuple):
+            low, high = temp
+            self._state = f"{int(low)}-{int(high)}"
+        else:
+            self._state = round(temp, 1)
diff --git a/homeassistant/components/nest/sensor_sdm.py b/homeassistant/components/nest/sensor_sdm.py
new file mode 100644
index 0000000000000000000000000000000000000000..8c567c9b36e7a2ab4aa8d8a7839ec34fb996fae0
--- /dev/null
+++ b/homeassistant/components/nest/sensor_sdm.py
@@ -0,0 +1,170 @@
+"""Support for Google Nest SDM sensors."""
+
+from typing import Optional
+
+from google_nest_sdm.device import Device
+from google_nest_sdm.device_traits import HumidityTrait, InfoTrait, TemperatureTrait
+
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.const import (
+    DEVICE_CLASS_HUMIDITY,
+    DEVICE_CLASS_TEMPERATURE,
+    PERCENTAGE,
+    TEMP_CELSIUS,
+)
+from homeassistant.helpers.dispatcher import async_dispatcher_connect
+from homeassistant.helpers.entity import Entity
+from homeassistant.helpers.typing import HomeAssistantType
+
+from .const import DOMAIN, SIGNAL_NEST_UPDATE
+
+
+async def async_setup_sdm_entry(
+    hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
+) -> None:
+    """Set up the sensors."""
+
+    subscriber = hass.data[DOMAIN][entry.entry_id]
+    device_manager = await subscriber.async_device_manager
+
+    # Fetch initial data so we have data when entities subscribe.
+
+    entities = []
+    for device in device_manager.devices.values():
+        if TemperatureTrait.NAME in device.traits:
+            entities.append(TemperatureSensor(device))
+        if HumidityTrait.NAME in device.traits:
+            entities.append(HumiditySensor(device))
+    async_add_entities(entities)
+
+
+class SensorBase(Entity):
+    """Representation of a dynamically updated Sensor."""
+
+    def __init__(self, device: Device):
+        """Initialize the sensor."""
+        self._device = device
+
+    @property
+    def should_pool(self) -> bool:
+        """Disable polling since entities have state pushed via pubsub."""
+        return False
+
+    @property
+    def unique_id(self) -> Optional[str]:
+        """Return a unique ID."""
+        # The API "name" field is a unique device identifier.
+        return f"{self._device.name}-{self.device_class}"
+
+    @property
+    def device_name(self):
+        """Return the name of the physical device that includes the sensor."""
+        if InfoTrait.NAME in self._device.traits:
+            trait = self._device.traits[InfoTrait.NAME]
+            if trait.custom_name:
+                return trait.custom_name
+        # Build a name from the room/structure.  Note: This room/structure name
+        # is not associated with a home assistant Area.
+        parent_relations = self._device.parent_relations
+        if parent_relations:
+            items = sorted(parent_relations.items())
+            names = [name for id, name in items]
+            return " ".join(names)
+        return self.unique_id
+
+    @property
+    def device_info(self):
+        """Return device specific attributes."""
+        return {
+            # The API "name" field is a unique device identifier.
+            "identifiers": {(DOMAIN, self._device.name)},
+            "name": self.device_name,
+            "manufacturer": "Google Nest",
+            "model": self.device_model,
+        }
+
+    @property
+    def device_model(self):
+        """Return device model information."""
+        # The API intentionally returns minimal information about specific
+        # devices, instead relying on traits, but we can infer a generic model
+        # name based on the type
+        if self._device.type == "sdm.devices.types.CAMERA":
+            return "Camera"
+        if self._device.type == "sdm.devices.types.DISPLAY":
+            return "Display"
+        if self._device.type == "sdm.devices.types.DOORBELL":
+            return "Doorbell"
+        if self._device.type == "sdm.devices.types.THERMOSTAT":
+            return "Thermostat"
+        return None
+
+    async def async_added_to_hass(self):
+        """Run when entity is added to register update signal handler."""
+
+        async def async_update_state():
+            """Update sensor state."""
+            await self.async_update_ha_state(True)
+
+        # Event messages trigger the SIGNAL_NEST_UPDATE, which is intercepted
+        # here to re-fresh the signals from _device.  Unregister this callback
+        # when the entity is removed.
+        self.async_on_remove(
+            async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, async_update_state)
+        )
+
+
+class TemperatureSensor(SensorBase):
+    """Representation of a Temperature Sensor."""
+
+    @property
+    def name(self):
+        """Return the name of the sensor."""
+        return f"{self.device_name} Temperature"
+
+    @property
+    def state(self):
+        """Return the state of the sensor."""
+        trait = self._device.traits[TemperatureTrait.NAME]
+        return trait.ambient_temperature_celsius
+
+    @property
+    def unit_of_measurement(self):
+        """Return the unit of measurement."""
+        return TEMP_CELSIUS
+
+    @property
+    def device_class(self):
+        """Return the class of this device."""
+        return DEVICE_CLASS_TEMPERATURE
+
+
+class HumiditySensor(SensorBase):
+    """Representation of a Humidity Sensor."""
+
+    @property
+    def unique_id(self) -> Optional[str]:
+        """Return a unique ID."""
+        # The API returns the identifier under the name field.
+        return f"{self._device.name}-humidity"
+
+    @property
+    def name(self):
+        """Return the name of the sensor."""
+        return f"{self.device_name} Humidity"
+
+    @property
+    def state(self):
+        """Return the state of the sensor."""
+        trait = self._device.traits[HumidityTrait.NAME]
+        return trait.ambient_humidity_percent
+
+    @property
+    def unit_of_measurement(self):
+        """Return the unit of measurement."""
+        return PERCENTAGE
+
+    @property
+    def device_class(self):
+        """Return the class of this device."""
+        return DEVICE_CLASS_HUMIDITY
diff --git a/homeassistant/components/nest/strings.json b/homeassistant/components/nest/strings.json
index 84e7885a363c00e5b3790338c2dc17d00af7f25e..943afd254c8d752c463b9801aab45d73afc8a732 100644
--- a/homeassistant/components/nest/strings.json
+++ b/homeassistant/components/nest/strings.json
@@ -1,6 +1,9 @@
 {
   "config": {
     "step": {
+      "pick_implementation": {
+        "title": "[%key:common::config_flow::title::oauth2_pick_implementation%]"
+      },
       "init": {
         "title": "Authentication Provider",
         "description": "[%key:common::config_flow::title::oauth2_pick_implementation%]",
@@ -23,6 +26,9 @@
       "missing_configuration": "[%key:common::config_flow::abort::oauth2_missing_configuration%]",
       "authorize_url_timeout": "[%key:common::config_flow::abort::oauth2_authorize_url_timeout%]",
       "authorize_url_fail": "Unknown error generating an authorize url."
+    },
+    "create_entry": {
+      "default": "[%key:common::config_flow::create_entry::authenticated%]"
     }
   }
 }
diff --git a/requirements_all.txt b/requirements_all.txt
index 44cc49f484bad9b1bea63ee39e86c1e6bfa16bf7..f899b56f85d228b0088f90c6630f35d7e84d0533 100644
--- a/requirements_all.txt
+++ b/requirements_all.txt
@@ -680,6 +680,9 @@ google-cloud-pubsub==0.39.1
 # homeassistant.components.google_cloud
 google-cloud-texttospeech==0.4.0
 
+# homeassistant.components.nest
+google-nest-sdm==0.1.6
+
 # homeassistant.components.google_travel_time
 googlemaps==2.5.1
 
diff --git a/requirements_test_all.txt b/requirements_test_all.txt
index 8beb06b0057ab9838beaed88171672314a622052..923fa5b373f89e77c764c4e285cd4e156514de49 100644
--- a/requirements_test_all.txt
+++ b/requirements_test_all.txt
@@ -348,6 +348,9 @@ google-api-python-client==1.6.4
 # homeassistant.components.google_pubsub
 google-cloud-pubsub==0.39.1
 
+# homeassistant.components.nest
+google-nest-sdm==0.1.6
+
 # homeassistant.components.gree
 greeclimate==0.9.0
 
diff --git a/tests/components/nest/sensor_sdm_test.py b/tests/components/nest/sensor_sdm_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..fbd13701260194244a26b87a07d1f3af8de552d0
--- /dev/null
+++ b/tests/components/nest/sensor_sdm_test.py
@@ -0,0 +1,248 @@
+"""
+Test for Nest sensors platform for the Smart Device Management API.
+
+These tests fake out the subscriber/devicemanager, and are not using a real
+pubsub subscriber.
+"""
+
+import time
+
+from google_nest_sdm.device import Device
+from google_nest_sdm.device_manager import DeviceManager
+from google_nest_sdm.event import EventCallback, EventMessage
+from google_nest_sdm.google_nest_subscriber import GoogleNestSubscriber
+
+from homeassistant.components.nest import DOMAIN
+from homeassistant.setup import async_setup_component
+
+from tests.async_mock import patch
+from tests.common import MockConfigEntry
+
+PLATFORM = "sensor"
+
+CONFIG = {
+    "nest": {
+        "client_id": "some-client-id",
+        "client_secret": "some-client-secret",
+        # Required fields for using SDM API
+        "project_id": "some-project-id",
+        "subscriber_id": "some-subscriber-id",
+    },
+}
+
+CONFIG_ENTRY_DATA = {
+    "sdm": {},  # Indicates new SDM API, not legacy API
+    "auth_implementation": "local",
+    "token": {
+        "expires_at": time.time() + 86400,
+        "access_token": {
+            "token": "some-token",
+        },
+    },
+}
+
+
+class FakeDeviceManager(DeviceManager):
+    """Fake DeviceManager that can supply a list of devices and structures."""
+
+    def __init__(self, devices: dict, structures: dict):
+        """Initialize FakeDeviceManager."""
+        super().__init__()
+        self._devices = devices
+
+    @property
+    def structures(self) -> dict:
+        """Override structures with fake result."""
+        return self._structures
+
+    @property
+    def devices(self) -> dict:
+        """Override devices with fake result."""
+        return self._devices
+
+
+class FakeSubscriber(GoogleNestSubscriber):
+    """Fake subscriber that supplies a FakeDeviceManager."""
+
+    def __init__(self, device_manager: FakeDeviceManager):
+        """Initialize Fake Subscriber."""
+        self._device_manager = device_manager
+        self._callback = None
+
+    def set_update_callback(self, callback: EventCallback):
+        """Capture the callback set by Home Assistant."""
+        self._callback = callback
+
+    async def start_async(self) -> DeviceManager:
+        """Return the fake device manager."""
+        return self._device_manager
+
+    @property
+    async def async_device_manager(self) -> DeviceManager:
+        """Return the fake device manager."""
+        return self._device_manager
+
+    def stop_async(self):
+        """No-op to stop the subscriber."""
+        return None
+
+    def receive_event(self, event_message: EventMessage):
+        """Simulate a received pubsub message, invoked by tests."""
+        # Update device state, then invoke HomeAssistant to refresh
+        self._device_manager.handle_event(event_message)
+        self._callback.handle_event(event_message)
+
+
+async def setup_sensor(hass, devices={}, structures={}):
+    """Set up the platform and prerequisites."""
+    MockConfigEntry(domain=DOMAIN, data=CONFIG_ENTRY_DATA).add_to_hass(hass)
+    device_manager = FakeDeviceManager(devices=devices, structures=structures)
+    subscriber = FakeSubscriber(device_manager)
+    with patch(
+        "homeassistant.helpers.config_entry_oauth2_flow.async_get_config_entry_implementation"
+    ), patch("homeassistant.components.nest.PLATFORMS", [PLATFORM]), patch(
+        "homeassistant.components.nest.GoogleNestSubscriber", return_value=subscriber
+    ):
+        assert await async_setup_component(hass, DOMAIN, CONFIG)
+    await hass.async_block_till_done()
+    return subscriber
+
+
+async def test_thermostat_device(hass):
+    """Test a thermostat with temperature and humidity sensors."""
+    devices = {
+        "some-device-id": Device.MakeDevice(
+            {
+                "name": "some-device-id",
+                "type": "sdm.devices.types.Thermostat",
+                "traits": {
+                    "sdm.devices.traits.Info": {
+                        "customName": "My Sensor",
+                    },
+                    "sdm.devices.traits.Temperature": {
+                        "ambientTemperatureCelsius": 25.1,
+                    },
+                    "sdm.devices.traits.Humidity": {
+                        "ambientHumidityPercent": 35.0,
+                    },
+                },
+            },
+            auth=None,
+        )
+    }
+    await setup_sensor(hass, devices)
+
+    temperature = hass.states.get("sensor.my_sensor_temperature")
+    assert temperature is not None
+    assert temperature.state == "25.1"
+
+    humidity = hass.states.get("sensor.my_sensor_humidity")
+    assert humidity is not None
+    assert humidity.state == "35.0"
+
+
+async def test_no_devices(hass):
+    """Test no devices returned by the api."""
+    await setup_sensor(hass)
+
+    temperature = hass.states.get("sensor.my_sensor_temperature")
+    assert temperature is None
+
+    humidity = hass.states.get("sensor.my_sensor_humidity")
+    assert humidity is None
+
+
+async def test_device_no_sensor_traits(hass):
+    """Test a device with applicable sensor traits."""
+    devices = {
+        "some-device-id": Device.MakeDevice(
+            {
+                "name": "some-device-id",
+                "type": "sdm.devices.types.Thermostat",
+                "traits": {},
+            },
+            auth=None,
+        )
+    }
+    await setup_sensor(hass, devices)
+
+    temperature = hass.states.get("sensor.my_sensor_temperature")
+    assert temperature is None
+
+    humidity = hass.states.get("sensor.my_sensor_humidity")
+    assert humidity is None
+
+
+async def test_device_name_from_structure(hass):
+    """Test a device without a custom name, inferring name from structure."""
+    devices = {
+        "some-device-id": Device.MakeDevice(
+            {
+                "name": "some-device-id",
+                "type": "sdm.devices.types.Thermostat",
+                "traits": {
+                    "sdm.devices.traits.Temperature": {
+                        "ambientTemperatureCelsius": 25.2,
+                    },
+                },
+                "parentRelations": [
+                    {"parent": "some-structure-id", "displayName": "Some Room"}
+                ],
+            },
+            auth=None,
+        )
+    }
+    await setup_sensor(hass, devices)
+
+    temperature = hass.states.get("sensor.some_room_temperature")
+    assert temperature is not None
+    assert temperature.state == "25.2"
+
+
+async def test_event_updates_sensor(hass):
+    """Test a pubsub message received by subscriber to update temperature."""
+    devices = {
+        "some-device-id": Device.MakeDevice(
+            {
+                "name": "some-device-id",
+                "type": "sdm.devices.types.Thermostat",
+                "traits": {
+                    "sdm.devices.traits.Info": {
+                        "customName": "My Sensor",
+                    },
+                    "sdm.devices.traits.Temperature": {
+                        "ambientTemperatureCelsius": 25.1,
+                    },
+                },
+            },
+            auth=None,
+        )
+    }
+    subscriber = await setup_sensor(hass, devices)
+
+    temperature = hass.states.get("sensor.my_sensor_temperature")
+    assert temperature is not None
+    assert temperature.state == "25.1"
+
+    # Simulate a pubsub message received by the subscriber with a trait update
+    event = EventMessage(
+        {
+            "eventId": "some-event-id",
+            "timestamp": "2019-01-01T00:00:01Z",
+            "resourceUpdate": {
+                "name": "some-device-id",
+                "traits": {
+                    "sdm.devices.traits.Temperature": {
+                        "ambientTemperatureCelsius": 26.2,
+                    },
+                },
+            },
+        },
+        auth=None,
+    )
+    subscriber.receive_event(event)
+    await hass.async_block_till_done()  # Process dispatch/update signal
+
+    temperature = hass.states.get("sensor.my_sensor_temperature")
+    assert temperature is not None
+    assert temperature.state == "26.2"
diff --git a/tests/components/nest/test_config_flow.py b/tests/components/nest/test_config_flow_legacy.py
similarity index 100%
rename from tests/components/nest/test_config_flow.py
rename to tests/components/nest/test_config_flow_legacy.py
diff --git a/tests/components/nest/test_config_flow_sdm.py b/tests/components/nest/test_config_flow_sdm.py
new file mode 100644
index 0000000000000000000000000000000000000000..1df751f39807b1448274b70386b37a6832aa3e53
--- /dev/null
+++ b/tests/components/nest/test_config_flow_sdm.py
@@ -0,0 +1,66 @@
+"""Test the Google Nest Device Access config flow."""
+from homeassistant import config_entries, setup
+from homeassistant.components.nest.const import DOMAIN, OAUTH2_AUTHORIZE, OAUTH2_TOKEN
+from homeassistant.const import CONF_CLIENT_ID, CONF_CLIENT_SECRET
+from homeassistant.helpers import config_entry_oauth2_flow
+
+from tests.async_mock import patch
+
+CLIENT_ID = "1234"
+CLIENT_SECRET = "5678"
+PROJECT_ID = "project-id-4321"
+SUBSCRIBER_ID = "subscriber-id-9876"
+
+
+async def test_full_flow(hass, aiohttp_client, aioclient_mock, current_request):
+    """Check full flow."""
+    assert await setup.async_setup_component(
+        hass,
+        DOMAIN,
+        {
+            DOMAIN: {
+                "project_id": PROJECT_ID,
+                "subscriber_id": SUBSCRIBER_ID,
+                CONF_CLIENT_ID: CLIENT_ID,
+                CONF_CLIENT_SECRET: CLIENT_SECRET,
+            },
+            "http": {"base_url": "https://example.com"},
+        },
+    )
+
+    result = await hass.config_entries.flow.async_init(
+        DOMAIN, context={"source": config_entries.SOURCE_USER}
+    )
+    state = config_entry_oauth2_flow._encode_jwt(hass, {"flow_id": result["flow_id"]})
+
+    oauth_authorize = OAUTH2_AUTHORIZE.format(project_id=PROJECT_ID)
+    assert result["url"] == (
+        f"{oauth_authorize}?response_type=code&client_id={CLIENT_ID}"
+        "&redirect_uri=https://example.com/auth/external/callback"
+        f"&state={state}&scope=https://www.googleapis.com/auth/sdm.service"
+        "+https://www.googleapis.com/auth/pubsub"
+        "&access_type=offline&prompt=consent"
+    )
+
+    client = await aiohttp_client(hass.http.app)
+    resp = await client.get(f"/auth/external/callback?code=abcd&state={state}")
+    assert resp.status == 200
+    assert resp.headers["content-type"] == "text/html; charset=utf-8"
+
+    aioclient_mock.post(
+        OAUTH2_TOKEN,
+        json={
+            "refresh_token": "mock-refresh-token",
+            "access_token": "mock-access-token",
+            "type": "Bearer",
+            "expires_in": 60,
+        },
+    )
+
+    with patch(
+        "homeassistant.components.nest.async_setup_entry", return_value=True
+    ) as mock_setup:
+        await hass.config_entries.flow.async_configure(result["flow_id"])
+
+    assert len(hass.config_entries.async_entries(DOMAIN)) == 1
+    assert len(mock_setup.mock_calls) == 1