1
0
mirror of https://github.com/home-assistant/core.git synced 2025-12-23 20:39:01 +00:00
Files
core/homeassistant/components/ecobee/__init__.py
Mark Coombes f6995b8d17 Add config flow to ecobee (#26634)
* Add basic config flow

* Fix json files

* Update __init__.py

* Fix json errors

* Move constants to const.py

* Add ecobee to generated config flows

* Update config_flow for updated API

* Update manifest to include new dependencies

Bump pyecobee, add aiofiles.

* Update constants for ecobee

* Modify ecobee setup to use config flow

* Bump dependency

* Update binary_sensor to use config_entry

* Update sensor to use config_entry

* Update __init__.py

* Update weather to use config_entry

* Update notify.py

* Update ecobee constants

* Update climate to use config_entry

* Avoid a breaking change on ecobee services

* Store api key from old config entry

* Allow unloading of config entry

* Show user a form before import

* Refine import flow

* Update strings.json to remove import step

Not needed.

* Move third party imports to top of module

* Remove periods from end of log messages

* Make configuration.yaml config optional

* Remove unused strings

* Reorganize config flow

* Remove unneeded requirement

* No need to store API key

* Update async_unload_entry

* Clean up if/else statements

* Update requirements_all.txt

* Fix config schema

* Update __init__.py

* Remove check for DATA_ECOBEE_CONFIG

* Remove redundant check

* Add check for DATA_ECOBEE_CONFIG

* Change setup_platform to async

* Fix state unknown and imports

* Change init step to user

* Have import step raise specific exceptions

* Rearrange try/except block in import flow

* Convert update() and refresh() to coroutines

...and update platforms to use async_update coroutine.

* Finish converting init to async

* Preliminary tests

* Test full implementation

* Update test_config_flow.py

* Update test_config_flow.py

* Add self to codeowners

* Update test_config_flow.py

* Use MockConfigEntry

* Update test_config_flow.py

* Update CODEOWNERS

* pylint fixes

* Register services under ecobee domain

Breaking change!

* Pylint fixes

* Pylint fixes

* Pylint fixes

* Move service strings to ecobee domain

* Fix log message capitalization

* Fix import formatting

* Update .coveragerc

* Add __init__ to coveragerc

* Add option flow test

* Update .coveragerc

* Act on updated options

* Revert "Act on updated options"

This reverts commit 56b0a859f2e3e80b6f4c77a8f784a2b29ee2cce9.

* Remove hold_temp from climate

* Remove hold_temp and options from init

* Remove options handler from config flow

* Remove options strings

* Remove options flow test

* Remove hold_temp constants

* Fix climate tests

* Pass api key to user step in import flow

* Update test_config_flow.py

Ensure that the import step calls the user step with the user's api key as user input if importing from ecobee.conf/validating imported keys fails.
2019-09-25 22:38:21 +02:00

131 lines
4.1 KiB
Python

"""Support for ecobee."""
import asyncio
from datetime import timedelta
import voluptuous as vol
from pyecobee import Ecobee, ECOBEE_API_KEY, ECOBEE_REFRESH_TOKEN, ExpiredTokenError
from homeassistant.config_entries import SOURCE_IMPORT
from homeassistant.const import CONF_API_KEY
from homeassistant.helpers import config_validation as cv
from homeassistant.util import Throttle
from .const import (
CONF_REFRESH_TOKEN,
DATA_ECOBEE_CONFIG,
DOMAIN,
ECOBEE_PLATFORMS,
_LOGGER,
)
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=180)
CONFIG_SCHEMA = vol.Schema(
{DOMAIN: vol.Schema({vol.Optional(CONF_API_KEY): cv.string})}, extra=vol.ALLOW_EXTRA
)
async def async_setup(hass, config):
"""
Ecobee uses config flow for configuration.
But, an "ecobee:" entry in configuration.yaml will trigger an import flow
if a config entry doesn't already exist. If ecobee.conf exists, the import
flow will attempt to import it and create a config entry, to assist users
migrating from the old ecobee component. Otherwise, the user will have to
continue setting up the integration via the config flow.
"""
hass.data[DATA_ECOBEE_CONFIG] = config.get(DOMAIN, {})
if not hass.config_entries.async_entries(DOMAIN) and hass.data[DATA_ECOBEE_CONFIG]:
# No config entry exists and configuration.yaml config exists, trigger the import flow.
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_IMPORT}
)
)
return True
async def async_setup_entry(hass, entry):
"""Set up ecobee via a config entry."""
api_key = entry.data[CONF_API_KEY]
refresh_token = entry.data[CONF_REFRESH_TOKEN]
data = EcobeeData(hass, entry, api_key=api_key, refresh_token=refresh_token)
if not await data.refresh():
return False
await data.update()
if data.ecobee.thermostats is None:
_LOGGER.error("No ecobee devices found to set up")
return False
hass.data[DOMAIN] = data
for component in ECOBEE_PLATFORMS:
hass.async_create_task(
hass.config_entries.async_forward_entry_setup(entry, component)
)
return True
class EcobeeData:
"""
Handle getting the latest data from ecobee.com so platforms can use it.
Also handle refreshing tokens and updating config entry with refreshed tokens.
"""
def __init__(self, hass, entry, api_key, refresh_token):
"""Initialize the Ecobee data object."""
self._hass = hass
self._entry = entry
self.ecobee = Ecobee(
config={ECOBEE_API_KEY: api_key, ECOBEE_REFRESH_TOKEN: refresh_token}
)
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def update(self):
"""Get the latest data from ecobee.com."""
try:
await self._hass.async_add_executor_job(self.ecobee.update)
_LOGGER.debug("Updating ecobee")
except ExpiredTokenError:
_LOGGER.warning(
"Ecobee update failed; attempting to refresh expired tokens"
)
await self.refresh()
async def refresh(self) -> bool:
"""Refresh ecobee tokens and update config entry."""
_LOGGER.debug("Refreshing ecobee tokens and updating config entry")
if await self._hass.async_add_executor_job(self.ecobee.refresh_tokens):
self._hass.config_entries.async_update_entry(
self._entry,
data={
CONF_API_KEY: self.ecobee.config[ECOBEE_API_KEY],
CONF_REFRESH_TOKEN: self.ecobee.config[ECOBEE_REFRESH_TOKEN],
},
)
return True
_LOGGER.error("Error updating ecobee tokens")
return False
async def async_unload_entry(hass, config_entry):
"""Unload the config entry and platforms."""
hass.data.pop(DOMAIN)
tasks = []
for platform in ECOBEE_PLATFORMS:
tasks.append(
hass.config_entries.async_forward_entry_unload(config_entry, platform)
)
return all(await asyncio.gather(*tasks))