diff --git a/.coveragerc b/.coveragerc
index a8459a2cd74762b3b8b95c7f30c8cea1f8468670..f637b64149000c9b959f56985fe4c25d7f3abe78 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -579,12 +579,9 @@ omit =
     homeassistant/components/nest/api.py
     homeassistant/components/nest/binary_sensor.py
     homeassistant/components/nest/camera.py
-    homeassistant/components/nest/camera_legacy.py
     homeassistant/components/nest/climate.py
-    homeassistant/components/nest/climate_legacy.py
-    homeassistant/components/nest/local_auth.py
+    homeassistant/components/nest/legacy/*
     homeassistant/components/nest/sensor.py
-    homeassistant/components/nest/sensor_legacy.py
     homeassistant/components/netatmo/__init__.py
     homeassistant/components/netatmo/api.py
     homeassistant/components/netatmo/camera.py
diff --git a/homeassistant/components/nest/__init__.py b/homeassistant/components/nest/__init__.py
index 151b1dac0009dcba94c73c9aa012362d4912614c..e9bffa2706c61378c1ad3b84fd9927519b7204de 100644
--- a/homeassistant/components/nest/__init__.py
+++ b/homeassistant/components/nest/__init__.py
@@ -1,45 +1,32 @@
 """Support for Nest devices."""
 
 import asyncio
-from datetime import datetime, timedelta
 import logging
-import threading
 
 from google_nest_sdm.event import AsyncEventCallback, EventMessage
 from google_nest_sdm.exceptions import AuthException, GoogleNestException
 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 SOURCE_REAUTH, ConfigEntry
 from homeassistant.const import (
     CONF_BINARY_SENSORS,
     CONF_CLIENT_ID,
     CONF_CLIENT_SECRET,
-    CONF_FILENAME,
     CONF_MONITORED_CONDITIONS,
     CONF_SENSORS,
     CONF_STRUCTURE,
-    EVENT_HOMEASSISTANT_START,
-    EVENT_HOMEASSISTANT_STOP,
 )
-from homeassistant.core import HomeAssistant, callback
+from homeassistant.core import HomeAssistant
 from homeassistant.exceptions import ConfigEntryNotReady
 from homeassistant.helpers import (
     aiohttp_client,
     config_entry_oauth2_flow,
     config_validation as cv,
 )
-from homeassistant.helpers.dispatcher import (
-    async_dispatcher_connect,
-    async_dispatcher_send,
-    dispatcher_send,
-)
-from homeassistant.helpers.entity import Entity
+from homeassistant.helpers.dispatcher import async_dispatcher_send
 
-from . import api, config_flow, local_auth
+from . import api, config_flow
 from .const import (
     API_URL,
     DATA_SDM,
@@ -50,34 +37,15 @@ from .const import (
     SIGNAL_NEST_UPDATE,
 )
 from .events import EVENT_NAME_MAP, NEST_EVENT
+from .legacy import async_setup_legacy, async_setup_legacy_entry
 
 _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"
 
-NEST_CONFIG_FILE = "nest.conf"
-
-ATTR_ETA = "eta"
-ATTR_ETA_WINDOW = "eta_window"
-ATTR_STRUCTURE = "structure"
-ATTR_TRIP_ID = "trip_id"
-
-AWAY_MODE_AWAY = "away"
-AWAY_MODE_HOME = "home"
-
-ATTR_AWAY_MODE = "away_mode"
-SERVICE_SET_AWAY_MODE = "set_away_mode"
-
 SENSOR_SCHEMA = vol.Schema(
     {vol.Optional(CONF_MONITORED_CONDITIONS): vol.All(cv.ensure_list)}
 )
@@ -104,31 +72,6 @@ CONFIG_SCHEMA = vol.Schema(
 # Platforms for SDM API
 PLATFORMS = ["sensor", "camera", "climate"]
 
-# Services for the legacy API
-
-SET_AWAY_MODE_SCHEMA = vol.Schema(
-    {
-        vol.Required(ATTR_AWAY_MODE): vol.In([AWAY_MODE_AWAY, AWAY_MODE_HOME]),
-        vol.Optional(ATTR_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
-    }
-)
-
-SET_ETA_SCHEMA = vol.Schema(
-    {
-        vol.Required(ATTR_ETA): cv.time_period,
-        vol.Optional(ATTR_TRIP_ID): cv.string,
-        vol.Optional(ATTR_ETA_WINDOW): cv.time_period,
-        vol.Optional(ATTR_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
-    }
-)
-
-CANCEL_ETA_SCHEMA = vol.Schema(
-    {
-        vol.Required(ATTR_TRIP_ID): cv.string,
-        vol.Optional(ATTR_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
-    }
-)
-
 
 async def async_setup(hass: HomeAssistant, config: dict):
     """Set up Nest components with dispatch between old/new flows."""
@@ -283,348 +226,3 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry):
         hass.data[DOMAIN].pop(DATA_SUBSCRIBER)
 
     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")
-
-    while hass.is_running:
-        nest.update_event.wait()
-
-        if not hass.is_running:
-            break
-
-        nest.update_event.clear()
-        _LOGGER.debug("Dispatching nest data update")
-        dispatcher_send(hass, SIGNAL_NEST_UPDATE)
-
-    _LOGGER.debug("Stop listening for nest.update_event")
-
-
-async def async_setup_legacy(hass, config):
-    """Set up Nest components using the legacy nest API."""
-    if DOMAIN not in config:
-        return True
-
-    conf = config[DOMAIN]
-
-    local_auth.initialize(hass, conf[CONF_CLIENT_ID], conf[CONF_CLIENT_SECRET])
-
-    filename = config.get(CONF_FILENAME, NEST_CONFIG_FILE)
-    access_token_cache_file = hass.config.path(filename)
-
-    hass.async_create_task(
-        hass.config_entries.flow.async_init(
-            DOMAIN,
-            context={"source": config_entries.SOURCE_IMPORT},
-            data={"nest_conf_path": access_token_cache_file},
-        )
-    )
-
-    # Store config to be used during entry setup
-    hass.data[DATA_NEST_CONFIG] = conf
-
-    return True
-
-
-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] = NestLegacyDevice(hass, conf, nest)
-    if not await hass.async_add_executor_job(hass.data[DATA_NEST].initialize):
-        return False
-
-    for component in "climate", "camera", "sensor", "binary_sensor":
-        hass.async_create_task(
-            hass.config_entries.async_forward_entry_setup(entry, component)
-        )
-
-    def validate_structures(target_structures):
-        all_structures = [structure.name for structure in nest.structures]
-        for target in target_structures:
-            if target not in all_structures:
-                _LOGGER.info("Invalid structure: %s", target)
-
-    def set_away_mode(service):
-        """Set the away mode for a Nest structure."""
-        if ATTR_STRUCTURE in service.data:
-            target_structures = service.data[ATTR_STRUCTURE]
-            validate_structures(target_structures)
-        else:
-            target_structures = hass.data[DATA_NEST].local_structure
-
-        for structure in nest.structures:
-            if structure.name in target_structures:
-                _LOGGER.info(
-                    "Setting away mode for: %s to: %s",
-                    structure.name,
-                    service.data[ATTR_AWAY_MODE],
-                )
-                structure.away = service.data[ATTR_AWAY_MODE]
-
-    def set_eta(service):
-        """Set away mode to away and include ETA for a Nest structure."""
-        if ATTR_STRUCTURE in service.data:
-            target_structures = service.data[ATTR_STRUCTURE]
-            validate_structures(target_structures)
-        else:
-            target_structures = hass.data[DATA_NEST].local_structure
-
-        for structure in nest.structures:
-            if structure.name in target_structures:
-                if structure.thermostats:
-                    _LOGGER.info(
-                        "Setting away mode for: %s to: %s",
-                        structure.name,
-                        AWAY_MODE_AWAY,
-                    )
-                    structure.away = AWAY_MODE_AWAY
-
-                    now = datetime.utcnow()
-                    trip_id = service.data.get(
-                        ATTR_TRIP_ID, f"trip_{int(now.timestamp())}"
-                    )
-                    eta_begin = now + service.data[ATTR_ETA]
-                    eta_window = service.data.get(ATTR_ETA_WINDOW, timedelta(minutes=1))
-                    eta_end = eta_begin + eta_window
-                    _LOGGER.info(
-                        "Setting ETA for trip: %s, "
-                        "ETA window starts at: %s and ends at: %s",
-                        trip_id,
-                        eta_begin,
-                        eta_end,
-                    )
-                    structure.set_eta(trip_id, eta_begin, eta_end)
-                else:
-                    _LOGGER.info(
-                        "No thermostats found in structure: %s, unable to set ETA",
-                        structure.name,
-                    )
-
-    def cancel_eta(service):
-        """Cancel ETA for a Nest structure."""
-        if ATTR_STRUCTURE in service.data:
-            target_structures = service.data[ATTR_STRUCTURE]
-            validate_structures(target_structures)
-        else:
-            target_structures = hass.data[DATA_NEST].local_structure
-
-        for structure in nest.structures:
-            if structure.name in target_structures:
-                if structure.thermostats:
-                    trip_id = service.data[ATTR_TRIP_ID]
-                    _LOGGER.info("Cancelling ETA for trip: %s", trip_id)
-                    structure.cancel_eta(trip_id)
-                else:
-                    _LOGGER.info(
-                        "No thermostats found in structure: %s, "
-                        "unable to cancel ETA",
-                        structure.name,
-                    )
-
-    hass.services.async_register(
-        DOMAIN, SERVICE_SET_AWAY_MODE, set_away_mode, schema=SET_AWAY_MODE_SCHEMA
-    )
-
-    hass.services.async_register(
-        DOMAIN, SERVICE_SET_ETA, set_eta, schema=SET_ETA_SCHEMA
-    )
-
-    hass.services.async_register(
-        DOMAIN, SERVICE_CANCEL_ETA, cancel_eta, schema=CANCEL_ETA_SCHEMA
-    )
-
-    @callback
-    def start_up(event):
-        """Start Nest update event listener."""
-        threading.Thread(
-            name="Nest update listener",
-            target=nest_update_event_broker,
-            args=(hass, nest),
-        ).start()
-
-    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_up)
-
-    @callback
-    def shut_down(event):
-        """Stop Nest update event listener."""
-        nest.update_event.set()
-
-    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shut_down)
-
-    _LOGGER.debug("async_setup_nest is done")
-
-    return True
-
-
-class NestLegacyDevice:
-    """Structure Nest functions for hass for legacy API."""
-
-    def __init__(self, hass, conf, nest):
-        """Init Nest Devices."""
-        self.hass = hass
-        self.nest = nest
-        self.local_structure = conf.get(CONF_STRUCTURE)
-
-    def initialize(self):
-        """Initialize Nest."""
-        try:
-            # Do not optimize next statement, it is here for initialize
-            # persistence Nest API connection.
-            structure_names = [s.name for s in self.nest.structures]
-            if self.local_structure is None:
-                self.local_structure = structure_names
-
-        except (AuthorizationError, APIError, OSError) as err:
-            _LOGGER.error("Connection error while access Nest web service: %s", err)
-            return False
-        return True
-
-    def structures(self):
-        """Generate a list of structures."""
-        try:
-            for structure in self.nest.structures:
-                if structure.name not in self.local_structure:
-                    _LOGGER.debug(
-                        "Ignoring structure %s, not in %s",
-                        structure.name,
-                        self.local_structure,
-                    )
-                    continue
-                yield structure
-
-        except (AuthorizationError, APIError, OSError) as err:
-            _LOGGER.error("Connection error while access Nest web service: %s", err)
-
-    def thermostats(self):
-        """Generate a list of thermostats."""
-        return self._devices("thermostats")
-
-    def smoke_co_alarms(self):
-        """Generate a list of smoke co alarms."""
-        return self._devices("smoke_co_alarms")
-
-    def cameras(self):
-        """Generate a list of cameras."""
-        return self._devices("cameras")
-
-    def _devices(self, device_type):
-        """Generate a list of Nest devices."""
-        try:
-            for structure in self.nest.structures:
-                if structure.name not in self.local_structure:
-                    _LOGGER.debug(
-                        "Ignoring structure %s, not in %s",
-                        structure.name,
-                        self.local_structure,
-                    )
-                    continue
-
-                for device in getattr(structure, device_type, []):
-                    try:
-                        # Do not optimize next statement,
-                        # it is here for verify Nest API permission.
-                        device.name_long
-                    except KeyError:
-                        _LOGGER.warning(
-                            "Cannot retrieve device name for [%s]"
-                            ", please check your Nest developer "
-                            "account permission settings",
-                            device.serial,
-                        )
-                        continue
-                    yield (structure, device)
-
-        except (AuthorizationError, APIError, OSError) as err:
-            _LOGGER.error("Connection error while access Nest web service: %s", err)
-
-
-class NestSensorDevice(Entity):
-    """Representation of a Nest sensor."""
-
-    def __init__(self, structure, device, variable):
-        """Initialize the sensor."""
-        self.structure = structure
-        self.variable = variable
-
-        if device is not None:
-            # device specific
-            self.device = device
-            self._name = f"{self.device.name_long} {self.variable.replace('_', ' ')}"
-        else:
-            # structure only
-            self.device = structure
-            self._name = f"{self.structure.name} {self.variable.replace('_', ' ')}"
-
-        self._state = None
-        self._unit = None
-
-    @property
-    def name(self):
-        """Return the name of the nest, if any."""
-        return self._name
-
-    @property
-    def unit_of_measurement(self):
-        """Return the unit the value is expressed in."""
-        return self._unit
-
-    @property
-    def should_poll(self):
-        """Do not need poll thanks using Nest streaming API."""
-        return False
-
-    @property
-    def unique_id(self):
-        """Return unique id based on device serial and variable."""
-        return f"{self.device.serial}-{self.variable}"
-
-    @property
-    def device_info(self):
-        """Return information about the device."""
-        if not hasattr(self.device, "name_long"):
-            name = self.structure.name
-            model = "Structure"
-        else:
-            name = self.device.name_long
-            if self.device.is_thermostat:
-                model = "Thermostat"
-            elif self.device.is_camera:
-                model = "Camera"
-            elif self.device.is_smoke_co_alarm:
-                model = "Nest Protect"
-            else:
-                model = None
-
-        return {
-            "identifiers": {(DOMAIN, self.device.serial)},
-            "name": name,
-            "manufacturer": "Nest Labs",
-            "model": model,
-        }
-
-    def update(self):
-        """Do not use NestSensorDevice directly."""
-        raise NotImplementedError
-
-    async def async_added_to_hass(self):
-        """Register update signal handler."""
-
-        async def async_update_state():
-            """Update sensor state."""
-            await self.async_update_ha_state(True)
-
-        self.async_on_remove(
-            async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, async_update_state)
-        )
diff --git a/homeassistant/components/nest/binary_sensor.py b/homeassistant/components/nest/binary_sensor.py
index 56d4ac31b787c63dc94adcd4bb0e373759277bf6..dc58dd2856fc8e4b4c84a4e10c2dd2346eceb5de 100644
--- a/homeassistant/components/nest/binary_sensor.py
+++ b/homeassistant/components/nest/binary_sensor.py
@@ -1,166 +1,15 @@
-"""Support for Nest Thermostat binary sensors."""
-from itertools import chain
-import logging
+"""Support for Nest binary sensors that dispatches between API versions."""
 
-from homeassistant.components.binary_sensor import (
-    DEVICE_CLASS_CONNECTIVITY,
-    DEVICE_CLASS_MOTION,
-    DEVICE_CLASS_OCCUPANCY,
-    DEVICE_CLASS_SOUND,
-    BinarySensorEntity,
-)
-from homeassistant.const import CONF_MONITORED_CONDITIONS
+from homeassistant.config_entries import ConfigEntry
+from homeassistant.helpers.typing import HomeAssistantType
 
-from . import CONF_BINARY_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice
+from .const import DATA_SDM
+from .legacy.sensor import async_setup_legacy_entry
 
-_LOGGER = logging.getLogger(__name__)
 
-BINARY_TYPES = {"online": DEVICE_CLASS_CONNECTIVITY}
-
-CLIMATE_BINARY_TYPES = {
-    "fan": None,
-    "is_using_emergency_heat": "heat",
-    "is_locked": None,
-    "has_leaf": None,
-}
-
-CAMERA_BINARY_TYPES = {
-    "motion_detected": DEVICE_CLASS_MOTION,
-    "sound_detected": DEVICE_CLASS_SOUND,
-    "person_detected": DEVICE_CLASS_OCCUPANCY,
-}
-
-STRUCTURE_BINARY_TYPES = {"away": None}
-
-STRUCTURE_BINARY_STATE_MAP = {"away": {"away": True, "home": False}}
-
-_BINARY_TYPES_DEPRECATED = [
-    "hvac_ac_state",
-    "hvac_aux_heater_state",
-    "hvac_heater_state",
-    "hvac_heat_x2_state",
-    "hvac_heat_x3_state",
-    "hvac_alt_heat_state",
-    "hvac_alt_heat_x2_state",
-    "hvac_emer_heat_state",
-]
-
-_VALID_BINARY_SENSOR_TYPES = {
-    **BINARY_TYPES,
-    **CLIMATE_BINARY_TYPES,
-    **CAMERA_BINARY_TYPES,
-    **STRUCTURE_BINARY_TYPES,
-}
-
-
-def setup_platform(hass, config, add_entities, discovery_info=None):
-    """Set up the Nest binary sensors.
-
-    No longer used.
-    """
-
-
-async def async_setup_entry(hass, entry, async_add_entities):
-    """Set up a Nest binary sensor based on a config entry."""
-    nest = hass.data[DATA_NEST]
-
-    discovery_info = hass.data.get(DATA_NEST_CONFIG, {}).get(CONF_BINARY_SENSORS, {})
-
-    # Add all available binary sensors if no Nest binary sensor config is set
-    if discovery_info == {}:
-        conditions = _VALID_BINARY_SENSOR_TYPES
-    else:
-        conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {})
-
-    for variable in conditions:
-        if variable in _BINARY_TYPES_DEPRECATED:
-            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_binary_sensors():
-        """Get the Nest binary sensors."""
-        sensors = []
-        for structure in nest.structures():
-            sensors += [
-                NestBinarySensor(structure, None, variable)
-                for variable in conditions
-                if variable in STRUCTURE_BINARY_TYPES
-            ]
-        device_chain = chain(nest.thermostats(), nest.smoke_co_alarms(), nest.cameras())
-        for structure, device in device_chain:
-            sensors += [
-                NestBinarySensor(structure, device, variable)
-                for variable in conditions
-                if variable in BINARY_TYPES
-            ]
-            sensors += [
-                NestBinarySensor(structure, device, variable)
-                for variable in conditions
-                if variable in CLIMATE_BINARY_TYPES and device.is_thermostat
-            ]
-
-            if device.is_camera:
-                sensors += [
-                    NestBinarySensor(structure, device, variable)
-                    for variable in conditions
-                    if variable in CAMERA_BINARY_TYPES
-                ]
-                for activity_zone in device.activity_zones:
-                    sensors += [
-                        NestActivityZoneSensor(structure, device, activity_zone)
-                    ]
-
-        return sensors
-
-    async_add_entities(await hass.async_add_executor_job(get_binary_sensors), True)
-
-
-class NestBinarySensor(NestSensorDevice, BinarySensorEntity):
-    """Represents a Nest binary sensor."""
-
-    @property
-    def is_on(self):
-        """Return true if the binary sensor is on."""
-        return self._state
-
-    @property
-    def device_class(self):
-        """Return the device class of the binary sensor."""
-        return _VALID_BINARY_SENSOR_TYPES.get(self.variable)
-
-    def update(self):
-        """Retrieve latest state."""
-        value = getattr(self.device, self.variable)
-        if self.variable in STRUCTURE_BINARY_TYPES:
-            self._state = bool(STRUCTURE_BINARY_STATE_MAP[self.variable].get(value))
-        else:
-            self._state = bool(value)
-
-
-class NestActivityZoneSensor(NestBinarySensor):
-    """Represents a Nest binary sensor for activity in a zone."""
-
-    def __init__(self, structure, device, zone):
-        """Initialize the sensor."""
-        super().__init__(structure, device, "")
-        self.zone = zone
-        self._name = f"{self._name} {self.zone.name} activity"
-
-    @property
-    def unique_id(self):
-        """Return unique id based on camera serial and zone id."""
-        return f"{self.device.serial}-{self.zone.zone_id}"
-
-    @property
-    def device_class(self):
-        """Return the device class of the binary sensor."""
-        return DEVICE_CLASS_MOTION
-
-    def update(self):
-        """Retrieve latest state."""
-        self._state = self.device.has_ongoing_motion_in_zone(self.zone.zone_id)
+async def async_setup_entry(
+    hass: HomeAssistantType, entry: ConfigEntry, async_add_entities
+) -> None:
+    """Set up the binary sensors."""
+    assert DATA_SDM not in entry.data
+    await async_setup_legacy_entry(hass, entry, async_add_entities)
diff --git a/homeassistant/components/nest/camera.py b/homeassistant/components/nest/camera.py
index dfa365a36c358df18cd719d87ffc7905b83ca6b5..f0e0b8e05fa1139470457a8813dc18e89f76877b 100644
--- a/homeassistant/components/nest/camera.py
+++ b/homeassistant/components/nest/camera.py
@@ -3,9 +3,9 @@
 from homeassistant.config_entries import ConfigEntry
 from homeassistant.helpers.typing import HomeAssistantType
 
-from .camera_legacy import async_setup_legacy_entry
 from .camera_sdm import async_setup_sdm_entry
 from .const import DATA_SDM
+from .legacy.camera import async_setup_legacy_entry
 
 
 async def async_setup_entry(
diff --git a/homeassistant/components/nest/climate.py b/homeassistant/components/nest/climate.py
index 6e457da039cb4aa8e61907457b9dcf5cb85061e0..a74a50b0f3615f7bcce6735d0a4c5fe3406aedd0 100644
--- a/homeassistant/components/nest/climate.py
+++ b/homeassistant/components/nest/climate.py
@@ -3,9 +3,9 @@
 from homeassistant.config_entries import ConfigEntry
 from homeassistant.helpers.typing import HomeAssistantType
 
-from .climate_legacy import async_setup_legacy_entry
 from .climate_sdm import async_setup_sdm_entry
 from .const import DATA_SDM
+from .legacy.climate import async_setup_legacy_entry
 
 
 async def async_setup_entry(
diff --git a/homeassistant/components/nest/legacy/__init__.py b/homeassistant/components/nest/legacy/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..218b01fd71b38d6cb3f69003a47aca3cf09332bf
--- /dev/null
+++ b/homeassistant/components/nest/legacy/__init__.py
@@ -0,0 +1,416 @@
+"""Support for Nest devices."""
+
+from datetime import datetime, timedelta
+import logging
+import threading
+
+from nest import Nest
+from nest.nest import APIError, AuthorizationError
+import voluptuous as vol
+
+from homeassistant import config_entries
+from homeassistant.const import (
+    CONF_CLIENT_ID,
+    CONF_CLIENT_SECRET,
+    CONF_FILENAME,
+    CONF_STRUCTURE,
+    EVENT_HOMEASSISTANT_START,
+    EVENT_HOMEASSISTANT_STOP,
+)
+from homeassistant.core import callback
+from homeassistant.helpers import 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 DATA_NEST, DATA_NEST_CONFIG, DOMAIN, SIGNAL_NEST_UPDATE
+
+_CONFIGURING = {}
+_LOGGER = logging.getLogger(__name__)
+
+# Configuration for the legacy nest API
+SERVICE_CANCEL_ETA = "cancel_eta"
+SERVICE_SET_ETA = "set_eta"
+
+NEST_CONFIG_FILE = "nest.conf"
+
+ATTR_ETA = "eta"
+ATTR_ETA_WINDOW = "eta_window"
+ATTR_STRUCTURE = "structure"
+ATTR_TRIP_ID = "trip_id"
+
+AWAY_MODE_AWAY = "away"
+AWAY_MODE_HOME = "home"
+
+ATTR_AWAY_MODE = "away_mode"
+SERVICE_SET_AWAY_MODE = "set_away_mode"
+
+# Services for the legacy API
+
+SET_AWAY_MODE_SCHEMA = vol.Schema(
+    {
+        vol.Required(ATTR_AWAY_MODE): vol.In([AWAY_MODE_AWAY, AWAY_MODE_HOME]),
+        vol.Optional(ATTR_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
+    }
+)
+
+SET_ETA_SCHEMA = vol.Schema(
+    {
+        vol.Required(ATTR_ETA): cv.time_period,
+        vol.Optional(ATTR_TRIP_ID): cv.string,
+        vol.Optional(ATTR_ETA_WINDOW): cv.time_period,
+        vol.Optional(ATTR_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
+    }
+)
+
+CANCEL_ETA_SCHEMA = vol.Schema(
+    {
+        vol.Required(ATTR_TRIP_ID): cv.string,
+        vol.Optional(ATTR_STRUCTURE): vol.All(cv.ensure_list, [cv.string]),
+    }
+)
+
+
+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")
+
+    while hass.is_running:
+        nest.update_event.wait()
+
+        if not hass.is_running:
+            break
+
+        nest.update_event.clear()
+        _LOGGER.debug("Dispatching nest data update")
+        dispatcher_send(hass, SIGNAL_NEST_UPDATE)
+
+    _LOGGER.debug("Stop listening for nest.update_event")
+
+
+async def async_setup_legacy(hass, config):
+    """Set up Nest components using the legacy nest API."""
+    if DOMAIN not in config:
+        return True
+
+    conf = config[DOMAIN]
+
+    local_auth.initialize(hass, conf[CONF_CLIENT_ID], conf[CONF_CLIENT_SECRET])
+
+    filename = config.get(CONF_FILENAME, NEST_CONFIG_FILE)
+    access_token_cache_file = hass.config.path(filename)
+
+    hass.async_create_task(
+        hass.config_entries.flow.async_init(
+            DOMAIN,
+            context={"source": config_entries.SOURCE_IMPORT},
+            data={"nest_conf_path": access_token_cache_file},
+        )
+    )
+
+    # Store config to be used during entry setup
+    hass.data[DATA_NEST_CONFIG] = conf
+
+    return True
+
+
+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] = NestLegacyDevice(hass, conf, nest)
+    if not await hass.async_add_executor_job(hass.data[DATA_NEST].initialize):
+        return False
+
+    for component in "climate", "camera", "sensor", "binary_sensor":
+        hass.async_create_task(
+            hass.config_entries.async_forward_entry_setup(entry, component)
+        )
+
+    def validate_structures(target_structures):
+        all_structures = [structure.name for structure in nest.structures]
+        for target in target_structures:
+            if target not in all_structures:
+                _LOGGER.info("Invalid structure: %s", target)
+
+    def set_away_mode(service):
+        """Set the away mode for a Nest structure."""
+        if ATTR_STRUCTURE in service.data:
+            target_structures = service.data[ATTR_STRUCTURE]
+            validate_structures(target_structures)
+        else:
+            target_structures = hass.data[DATA_NEST].local_structure
+
+        for structure in nest.structures:
+            if structure.name in target_structures:
+                _LOGGER.info(
+                    "Setting away mode for: %s to: %s",
+                    structure.name,
+                    service.data[ATTR_AWAY_MODE],
+                )
+                structure.away = service.data[ATTR_AWAY_MODE]
+
+    def set_eta(service):
+        """Set away mode to away and include ETA for a Nest structure."""
+        if ATTR_STRUCTURE in service.data:
+            target_structures = service.data[ATTR_STRUCTURE]
+            validate_structures(target_structures)
+        else:
+            target_structures = hass.data[DATA_NEST].local_structure
+
+        for structure in nest.structures:
+            if structure.name in target_structures:
+                if structure.thermostats:
+                    _LOGGER.info(
+                        "Setting away mode for: %s to: %s",
+                        structure.name,
+                        AWAY_MODE_AWAY,
+                    )
+                    structure.away = AWAY_MODE_AWAY
+
+                    now = datetime.utcnow()
+                    trip_id = service.data.get(
+                        ATTR_TRIP_ID, f"trip_{int(now.timestamp())}"
+                    )
+                    eta_begin = now + service.data[ATTR_ETA]
+                    eta_window = service.data.get(ATTR_ETA_WINDOW, timedelta(minutes=1))
+                    eta_end = eta_begin + eta_window
+                    _LOGGER.info(
+                        "Setting ETA for trip: %s, "
+                        "ETA window starts at: %s and ends at: %s",
+                        trip_id,
+                        eta_begin,
+                        eta_end,
+                    )
+                    structure.set_eta(trip_id, eta_begin, eta_end)
+                else:
+                    _LOGGER.info(
+                        "No thermostats found in structure: %s, unable to set ETA",
+                        structure.name,
+                    )
+
+    def cancel_eta(service):
+        """Cancel ETA for a Nest structure."""
+        if ATTR_STRUCTURE in service.data:
+            target_structures = service.data[ATTR_STRUCTURE]
+            validate_structures(target_structures)
+        else:
+            target_structures = hass.data[DATA_NEST].local_structure
+
+        for structure in nest.structures:
+            if structure.name in target_structures:
+                if structure.thermostats:
+                    trip_id = service.data[ATTR_TRIP_ID]
+                    _LOGGER.info("Cancelling ETA for trip: %s", trip_id)
+                    structure.cancel_eta(trip_id)
+                else:
+                    _LOGGER.info(
+                        "No thermostats found in structure: %s, "
+                        "unable to cancel ETA",
+                        structure.name,
+                    )
+
+    hass.services.async_register(
+        DOMAIN, SERVICE_SET_AWAY_MODE, set_away_mode, schema=SET_AWAY_MODE_SCHEMA
+    )
+
+    hass.services.async_register(
+        DOMAIN, SERVICE_SET_ETA, set_eta, schema=SET_ETA_SCHEMA
+    )
+
+    hass.services.async_register(
+        DOMAIN, SERVICE_CANCEL_ETA, cancel_eta, schema=CANCEL_ETA_SCHEMA
+    )
+
+    @callback
+    def start_up(event):
+        """Start Nest update event listener."""
+        threading.Thread(
+            name="Nest update listener",
+            target=nest_update_event_broker,
+            args=(hass, nest),
+        ).start()
+
+    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, start_up)
+
+    @callback
+    def shut_down(event):
+        """Stop Nest update event listener."""
+        nest.update_event.set()
+
+    hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, shut_down)
+
+    _LOGGER.debug("async_setup_nest is done")
+
+    return True
+
+
+class NestLegacyDevice:
+    """Structure Nest functions for hass for legacy API."""
+
+    def __init__(self, hass, conf, nest):
+        """Init Nest Devices."""
+        self.hass = hass
+        self.nest = nest
+        self.local_structure = conf.get(CONF_STRUCTURE)
+
+    def initialize(self):
+        """Initialize Nest."""
+        try:
+            # Do not optimize next statement, it is here for initialize
+            # persistence Nest API connection.
+            structure_names = [s.name for s in self.nest.structures]
+            if self.local_structure is None:
+                self.local_structure = structure_names
+
+        except (AuthorizationError, APIError, OSError) as err:
+            _LOGGER.error("Connection error while access Nest web service: %s", err)
+            return False
+        return True
+
+    def structures(self):
+        """Generate a list of structures."""
+        try:
+            for structure in self.nest.structures:
+                if structure.name not in self.local_structure:
+                    _LOGGER.debug(
+                        "Ignoring structure %s, not in %s",
+                        structure.name,
+                        self.local_structure,
+                    )
+                    continue
+                yield structure
+
+        except (AuthorizationError, APIError, OSError) as err:
+            _LOGGER.error("Connection error while access Nest web service: %s", err)
+
+    def thermostats(self):
+        """Generate a list of thermostats."""
+        return self._devices("thermostats")
+
+    def smoke_co_alarms(self):
+        """Generate a list of smoke co alarms."""
+        return self._devices("smoke_co_alarms")
+
+    def cameras(self):
+        """Generate a list of cameras."""
+        return self._devices("cameras")
+
+    def _devices(self, device_type):
+        """Generate a list of Nest devices."""
+        try:
+            for structure in self.nest.structures:
+                if structure.name not in self.local_structure:
+                    _LOGGER.debug(
+                        "Ignoring structure %s, not in %s",
+                        structure.name,
+                        self.local_structure,
+                    )
+                    continue
+
+                for device in getattr(structure, device_type, []):
+                    try:
+                        # Do not optimize next statement,
+                        # it is here for verify Nest API permission.
+                        device.name_long
+                    except KeyError:
+                        _LOGGER.warning(
+                            "Cannot retrieve device name for [%s]"
+                            ", please check your Nest developer "
+                            "account permission settings",
+                            device.serial,
+                        )
+                        continue
+                    yield (structure, device)
+
+        except (AuthorizationError, APIError, OSError) as err:
+            _LOGGER.error("Connection error while access Nest web service: %s", err)
+
+
+class NestSensorDevice(Entity):
+    """Representation of a Nest sensor."""
+
+    def __init__(self, structure, device, variable):
+        """Initialize the sensor."""
+        self.structure = structure
+        self.variable = variable
+
+        if device is not None:
+            # device specific
+            self.device = device
+            self._name = f"{self.device.name_long} {self.variable.replace('_', ' ')}"
+        else:
+            # structure only
+            self.device = structure
+            self._name = f"{self.structure.name} {self.variable.replace('_', ' ')}"
+
+        self._state = None
+        self._unit = None
+
+    @property
+    def name(self):
+        """Return the name of the nest, if any."""
+        return self._name
+
+    @property
+    def unit_of_measurement(self):
+        """Return the unit the value is expressed in."""
+        return self._unit
+
+    @property
+    def should_poll(self):
+        """Do not need poll thanks using Nest streaming API."""
+        return False
+
+    @property
+    def unique_id(self):
+        """Return unique id based on device serial and variable."""
+        return f"{self.device.serial}-{self.variable}"
+
+    @property
+    def device_info(self):
+        """Return information about the device."""
+        if not hasattr(self.device, "name_long"):
+            name = self.structure.name
+            model = "Structure"
+        else:
+            name = self.device.name_long
+            if self.device.is_thermostat:
+                model = "Thermostat"
+            elif self.device.is_camera:
+                model = "Camera"
+            elif self.device.is_smoke_co_alarm:
+                model = "Nest Protect"
+            else:
+                model = None
+
+        return {
+            "identifiers": {(DOMAIN, self.device.serial)},
+            "name": name,
+            "manufacturer": "Nest Labs",
+            "model": model,
+        }
+
+    def update(self):
+        """Do not use NestSensorDevice directly."""
+        raise NotImplementedError
+
+    async def async_added_to_hass(self):
+        """Register update signal handler."""
+
+        async def async_update_state():
+            """Update sensor state."""
+            await self.async_update_ha_state(True)
+
+        self.async_on_remove(
+            async_dispatcher_connect(self.hass, SIGNAL_NEST_UPDATE, async_update_state)
+        )
diff --git a/homeassistant/components/nest/legacy/binary_sensor.py b/homeassistant/components/nest/legacy/binary_sensor.py
new file mode 100644
index 0000000000000000000000000000000000000000..4470bd14676436b71c600b7157508725d613318a
--- /dev/null
+++ b/homeassistant/components/nest/legacy/binary_sensor.py
@@ -0,0 +1,167 @@
+"""Support for Nest Thermostat binary sensors."""
+from itertools import chain
+import logging
+
+from homeassistant.components.binary_sensor import (
+    DEVICE_CLASS_CONNECTIVITY,
+    DEVICE_CLASS_MOTION,
+    DEVICE_CLASS_OCCUPANCY,
+    DEVICE_CLASS_SOUND,
+    BinarySensorEntity,
+)
+from homeassistant.const import CONF_BINARY_SENSORS, CONF_MONITORED_CONDITIONS
+
+from . import NestSensorDevice
+from .const import DATA_NEST, DATA_NEST_CONFIG
+
+_LOGGER = logging.getLogger(__name__)
+
+BINARY_TYPES = {"online": DEVICE_CLASS_CONNECTIVITY}
+
+CLIMATE_BINARY_TYPES = {
+    "fan": None,
+    "is_using_emergency_heat": "heat",
+    "is_locked": None,
+    "has_leaf": None,
+}
+
+CAMERA_BINARY_TYPES = {
+    "motion_detected": DEVICE_CLASS_MOTION,
+    "sound_detected": DEVICE_CLASS_SOUND,
+    "person_detected": DEVICE_CLASS_OCCUPANCY,
+}
+
+STRUCTURE_BINARY_TYPES = {"away": None}
+
+STRUCTURE_BINARY_STATE_MAP = {"away": {"away": True, "home": False}}
+
+_BINARY_TYPES_DEPRECATED = [
+    "hvac_ac_state",
+    "hvac_aux_heater_state",
+    "hvac_heater_state",
+    "hvac_heat_x2_state",
+    "hvac_heat_x3_state",
+    "hvac_alt_heat_state",
+    "hvac_alt_heat_x2_state",
+    "hvac_emer_heat_state",
+]
+
+_VALID_BINARY_SENSOR_TYPES = {
+    **BINARY_TYPES,
+    **CLIMATE_BINARY_TYPES,
+    **CAMERA_BINARY_TYPES,
+    **STRUCTURE_BINARY_TYPES,
+}
+
+
+def setup_platform(hass, config, add_entities, discovery_info=None):
+    """Set up the Nest binary sensors.
+
+    No longer used.
+    """
+
+
+async def async_setup_entry(hass, entry, async_add_entities):
+    """Set up a Nest binary sensor based on a config entry."""
+    nest = hass.data[DATA_NEST]
+
+    discovery_info = hass.data.get(DATA_NEST_CONFIG, {}).get(CONF_BINARY_SENSORS, {})
+
+    # Add all available binary sensors if no Nest binary sensor config is set
+    if discovery_info == {}:
+        conditions = _VALID_BINARY_SENSOR_TYPES
+    else:
+        conditions = discovery_info.get(CONF_MONITORED_CONDITIONS, {})
+
+    for variable in conditions:
+        if variable in _BINARY_TYPES_DEPRECATED:
+            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_binary_sensors():
+        """Get the Nest binary sensors."""
+        sensors = []
+        for structure in nest.structures():
+            sensors += [
+                NestBinarySensor(structure, None, variable)
+                for variable in conditions
+                if variable in STRUCTURE_BINARY_TYPES
+            ]
+        device_chain = chain(nest.thermostats(), nest.smoke_co_alarms(), nest.cameras())
+        for structure, device in device_chain:
+            sensors += [
+                NestBinarySensor(structure, device, variable)
+                for variable in conditions
+                if variable in BINARY_TYPES
+            ]
+            sensors += [
+                NestBinarySensor(structure, device, variable)
+                for variable in conditions
+                if variable in CLIMATE_BINARY_TYPES and device.is_thermostat
+            ]
+
+            if device.is_camera:
+                sensors += [
+                    NestBinarySensor(structure, device, variable)
+                    for variable in conditions
+                    if variable in CAMERA_BINARY_TYPES
+                ]
+                for activity_zone in device.activity_zones:
+                    sensors += [
+                        NestActivityZoneSensor(structure, device, activity_zone)
+                    ]
+
+        return sensors
+
+    async_add_entities(await hass.async_add_executor_job(get_binary_sensors), True)
+
+
+class NestBinarySensor(NestSensorDevice, BinarySensorEntity):
+    """Represents a Nest binary sensor."""
+
+    @property
+    def is_on(self):
+        """Return true if the binary sensor is on."""
+        return self._state
+
+    @property
+    def device_class(self):
+        """Return the device class of the binary sensor."""
+        return _VALID_BINARY_SENSOR_TYPES.get(self.variable)
+
+    def update(self):
+        """Retrieve latest state."""
+        value = getattr(self.device, self.variable)
+        if self.variable in STRUCTURE_BINARY_TYPES:
+            self._state = bool(STRUCTURE_BINARY_STATE_MAP[self.variable].get(value))
+        else:
+            self._state = bool(value)
+
+
+class NestActivityZoneSensor(NestBinarySensor):
+    """Represents a Nest binary sensor for activity in a zone."""
+
+    def __init__(self, structure, device, zone):
+        """Initialize the sensor."""
+        super().__init__(structure, device, "")
+        self.zone = zone
+        self._name = f"{self._name} {self.zone.name} activity"
+
+    @property
+    def unique_id(self):
+        """Return unique id based on camera serial and zone id."""
+        return f"{self.device.serial}-{self.zone.zone_id}"
+
+    @property
+    def device_class(self):
+        """Return the device class of the binary sensor."""
+        return DEVICE_CLASS_MOTION
+
+    def update(self):
+        """Retrieve latest state."""
+        self._state = self.device.has_ongoing_motion_in_zone(self.zone.zone_id)
diff --git a/homeassistant/components/nest/camera_legacy.py b/homeassistant/components/nest/legacy/camera.py
similarity index 95%
rename from homeassistant/components/nest/camera_legacy.py
rename to homeassistant/components/nest/legacy/camera.py
index 48d9cb00783869a1e9372e11e021268edda04747..cc9be9d75883e98c2644e09abe90038365ae4630 100644
--- a/homeassistant/components/nest/camera_legacy.py
+++ b/homeassistant/components/nest/legacy/camera.py
@@ -4,10 +4,11 @@ import logging
 
 import requests
 
-from homeassistant.components import nest
 from homeassistant.components.camera import PLATFORM_SCHEMA, SUPPORT_ON_OFF, Camera
 from homeassistant.util.dt import utcnow
 
+from .const import DATA_NEST, DOMAIN
+
 _LOGGER = logging.getLogger(__name__)
 
 NEST_BRAND = "Nest"
@@ -24,9 +25,7 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
 
 async def async_setup_legacy_entry(hass, entry, async_add_entities):
     """Set up a Nest sensor based on a config entry."""
-    camera_devices = await hass.async_add_executor_job(
-        hass.data[nest.DATA_NEST].cameras
-    )
+    camera_devices = await hass.async_add_executor_job(hass.data[DATA_NEST].cameras)
     cameras = [NestCamera(structure, device) for structure, device in camera_devices]
     async_add_entities(cameras, True)
 
@@ -63,7 +62,7 @@ class NestCamera(Camera):
     def device_info(self):
         """Return information about the device."""
         return {
-            "identifiers": {(nest.DOMAIN, self.device.device_id)},
+            "identifiers": {(DOMAIN, self.device.device_id)},
             "name": self.device.name_long,
             "manufacturer": "Nest Labs",
             "model": "Camera",
diff --git a/homeassistant/components/nest/climate_legacy.py b/homeassistant/components/nest/legacy/climate.py
similarity index 98%
rename from homeassistant/components/nest/climate_legacy.py
rename to homeassistant/components/nest/legacy/climate.py
index ee28a0905c3fb2d5ba78da68c645e454707cf2f7..cd0d66acba87ef5c0abb552b36c395717f68ae1b 100644
--- a/homeassistant/components/nest/climate_legacy.py
+++ b/homeassistant/components/nest/legacy/climate.py
@@ -1,4 +1,4 @@
-"""Support for Nest thermostats."""
+"""Legacy Works with Nest climate implementation."""
 import logging
 
 from nest.nest import APIError
@@ -33,8 +33,7 @@ from homeassistant.const import (
 )
 from homeassistant.helpers.dispatcher import async_dispatcher_connect
 
-from . import DATA_NEST, DOMAIN as NEST_DOMAIN
-from .const import SIGNAL_NEST_UPDATE
+from .const import DATA_NEST, DOMAIN, SIGNAL_NEST_UPDATE
 
 _LOGGER = logging.getLogger(__name__)
 
@@ -170,7 +169,7 @@ class NestThermostat(ClimateEntity):
     def device_info(self):
         """Return information about the device."""
         return {
-            "identifiers": {(NEST_DOMAIN, self.device.device_id)},
+            "identifiers": {(DOMAIN, self.device.device_id)},
             "name": self.device.name_long,
             "manufacturer": "Nest Labs",
             "model": "Thermostat",
diff --git a/homeassistant/components/nest/legacy/const.py b/homeassistant/components/nest/legacy/const.py
new file mode 100644
index 0000000000000000000000000000000000000000..664606b9edc541697c8106e61eb64e5c1925fd6c
--- /dev/null
+++ b/homeassistant/components/nest/legacy/const.py
@@ -0,0 +1,6 @@
+"""Constants used by the legacy Nest component."""
+
+DOMAIN = "nest"
+DATA_NEST = "nest"
+DATA_NEST_CONFIG = "nest_config"
+SIGNAL_NEST_UPDATE = "nest_update"
diff --git a/homeassistant/components/nest/local_auth.py b/homeassistant/components/nest/legacy/local_auth.py
similarity index 85%
rename from homeassistant/components/nest/local_auth.py
rename to homeassistant/components/nest/legacy/local_auth.py
index 8be2693325e030e3be5c5c5d60ccbb71f4478b03..f5fb286df7e8451ebfc797c7feabcb82147e2e19 100644
--- a/homeassistant/components/nest/local_auth.py
+++ b/homeassistant/components/nest/legacy/local_auth.py
@@ -7,14 +7,14 @@ from nest.nest import AUTHORIZE_URL, AuthorizationError, NestAuth
 from homeassistant.const import HTTP_UNAUTHORIZED
 from homeassistant.core import callback
 
-from . import config_flow
+from ..config_flow import CodeInvalid, NestAuthError, register_flow_implementation
 from .const import DOMAIN
 
 
 @callback
 def initialize(hass, client_id, client_secret):
     """Initialize a local auth provider."""
-    config_flow.register_flow_implementation(
+    register_flow_implementation(
         hass,
         DOMAIN,
         "configuration.yaml",
@@ -44,7 +44,7 @@ async def resolve_auth_code(hass, client_id, client_secret, code):
         return await result
     except AuthorizationError as err:
         if err.response.status_code == HTTP_UNAUTHORIZED:
-            raise config_flow.CodeInvalid()
-        raise config_flow.NestAuthError(
+            raise CodeInvalid() from err
+        raise NestAuthError(
             f"Unknown error: {err} ({err.response.status_code})"
-        )
+        ) from err
diff --git a/homeassistant/components/nest/sensor_legacy.py b/homeassistant/components/nest/legacy/sensor.py
similarity index 98%
rename from homeassistant/components/nest/sensor_legacy.py
rename to homeassistant/components/nest/legacy/sensor.py
index 2df668513e16146bf6b6c5e5bc27d159295f1e0b..34f525ca7a626b836e96ee14bd547dc4bb572ddd 100644
--- a/homeassistant/components/nest/sensor_legacy.py
+++ b/homeassistant/components/nest/legacy/sensor.py
@@ -3,6 +3,7 @@ import logging
 
 from homeassistant.const import (
     CONF_MONITORED_CONDITIONS,
+    CONF_SENSORS,
     DEVICE_CLASS_HUMIDITY,
     DEVICE_CLASS_TEMPERATURE,
     PERCENTAGE,
@@ -11,7 +12,8 @@ from homeassistant.const import (
     TEMP_FAHRENHEIT,
 )
 
-from . import CONF_SENSORS, DATA_NEST, DATA_NEST_CONFIG, NestSensorDevice
+from . import NestSensorDevice
+from .const import DATA_NEST, DATA_NEST_CONFIG
 
 SENSOR_TYPES = ["humidity", "operation_mode", "hvac_state"]
 
diff --git a/homeassistant/components/nest/sensor.py b/homeassistant/components/nest/sensor.py
index 6245c5d83d02f5323fc8039fe8efd6b4ae764d61..0dcc89e2262ccde99f5547e4b83d6c3c1f7e0493 100644
--- a/homeassistant/components/nest/sensor.py
+++ b/homeassistant/components/nest/sensor.py
@@ -4,7 +4,7 @@ from homeassistant.config_entries import ConfigEntry
 from homeassistant.helpers.typing import HomeAssistantType
 
 from .const import DATA_SDM
-from .sensor_legacy import async_setup_legacy_entry
+from .legacy.sensor import async_setup_legacy_entry
 from .sensor_sdm import async_setup_sdm_entry
 
 
diff --git a/tests/components/nest/test_local_auth.py b/tests/components/nest/test_local_auth.py
index 491b9bd9e0701ee95e263936492d1ad0d0895a50..ecc37bbe2446bb000d4a4cd2c4091d41ea7a2155 100644
--- a/tests/components/nest/test_local_auth.py
+++ b/tests/components/nest/test_local_auth.py
@@ -4,7 +4,8 @@ from urllib.parse import parse_qsl
 import pytest
 import requests_mock as rmock
 
-from homeassistant.components.nest import config_flow, const, local_auth
+from homeassistant.components.nest import config_flow, const
+from homeassistant.components.nest.legacy import local_auth
 
 
 @pytest.fixture