* 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.
120 lines
4.4 KiB
Python
120 lines
4.4 KiB
Python
"""Config flow to configure ecobee."""
|
|
import voluptuous as vol
|
|
|
|
from pyecobee import (
|
|
Ecobee,
|
|
ECOBEE_CONFIG_FILENAME,
|
|
ECOBEE_API_KEY,
|
|
ECOBEE_REFRESH_TOKEN,
|
|
)
|
|
|
|
from homeassistant import config_entries
|
|
from homeassistant.const import CONF_API_KEY
|
|
from homeassistant.core import HomeAssistantError
|
|
from homeassistant.util.json import load_json
|
|
|
|
from .const import CONF_REFRESH_TOKEN, DATA_ECOBEE_CONFIG, DOMAIN, _LOGGER
|
|
|
|
|
|
class EcobeeFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
|
|
"""Handle an ecobee config flow."""
|
|
|
|
VERSION = 1
|
|
CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL
|
|
|
|
def __init__(self):
|
|
"""Initialize the ecobee flow."""
|
|
self._ecobee = None
|
|
|
|
async def async_step_user(self, user_input=None):
|
|
"""Handle a flow initiated by the user."""
|
|
if self._async_current_entries():
|
|
# Config entry already exists, only one allowed.
|
|
return self.async_abort(reason="one_instance_only")
|
|
|
|
errors = {}
|
|
stored_api_key = self.hass.data[DATA_ECOBEE_CONFIG].get(CONF_API_KEY)
|
|
|
|
if user_input is not None:
|
|
# Use the user-supplied API key to attempt to obtain a PIN from ecobee.
|
|
self._ecobee = Ecobee(config={ECOBEE_API_KEY: user_input[CONF_API_KEY]})
|
|
|
|
if await self.hass.async_add_executor_job(self._ecobee.request_pin):
|
|
# We have a PIN; move to the next step of the flow.
|
|
return await self.async_step_authorize()
|
|
errors["base"] = "pin_request_failed"
|
|
|
|
return self.async_show_form(
|
|
step_id="user",
|
|
data_schema=vol.Schema(
|
|
{vol.Required(CONF_API_KEY, default=stored_api_key): str}
|
|
),
|
|
errors=errors,
|
|
)
|
|
|
|
async def async_step_authorize(self, user_input=None):
|
|
"""Present the user with the PIN so that the app can be authorized on ecobee.com."""
|
|
errors = {}
|
|
|
|
if user_input is not None:
|
|
# Attempt to obtain tokens from ecobee and finish the flow.
|
|
if await self.hass.async_add_executor_job(self._ecobee.request_tokens):
|
|
# Refresh token obtained; create the config entry.
|
|
config = {
|
|
CONF_API_KEY: self._ecobee.api_key,
|
|
CONF_REFRESH_TOKEN: self._ecobee.refresh_token,
|
|
}
|
|
return self.async_create_entry(title=DOMAIN, data=config)
|
|
errors["base"] = "token_request_failed"
|
|
|
|
return self.async_show_form(
|
|
step_id="authorize",
|
|
errors=errors,
|
|
description_placeholders={"pin": self._ecobee.pin},
|
|
)
|
|
|
|
async def async_step_import(self, import_data):
|
|
"""
|
|
Import ecobee config from configuration.yaml.
|
|
|
|
Triggered by async_setup only if a config entry doesn't already exist.
|
|
If ecobee.conf exists, we will attempt to validate the credentials
|
|
and create an entry if valid. Otherwise, we will delegate to the user
|
|
step so that the user can continue the config flow.
|
|
"""
|
|
try:
|
|
legacy_config = await self.hass.async_add_executor_job(
|
|
load_json, self.hass.config.path(ECOBEE_CONFIG_FILENAME)
|
|
)
|
|
config = {
|
|
ECOBEE_API_KEY: legacy_config[ECOBEE_API_KEY],
|
|
ECOBEE_REFRESH_TOKEN: legacy_config[ECOBEE_REFRESH_TOKEN],
|
|
}
|
|
except (HomeAssistantError, KeyError):
|
|
_LOGGER.debug(
|
|
"No valid ecobee.conf configuration found for import, delegating to user step"
|
|
)
|
|
return await self.async_step_user(
|
|
user_input={
|
|
CONF_API_KEY: self.hass.data[DATA_ECOBEE_CONFIG].get(CONF_API_KEY)
|
|
}
|
|
)
|
|
|
|
ecobee = Ecobee(config=config)
|
|
if await self.hass.async_add_executor_job(ecobee.refresh_tokens):
|
|
# Credentials found and validated; create the entry.
|
|
_LOGGER.debug(
|
|
"Valid ecobee configuration found for import, creating config entry"
|
|
)
|
|
return self.async_create_entry(
|
|
title=DOMAIN,
|
|
data={
|
|
CONF_API_KEY: ecobee.api_key,
|
|
CONF_REFRESH_TOKEN: ecobee.refresh_token,
|
|
},
|
|
)
|
|
return await self.async_step_user(
|
|
user_input={
|
|
CONF_API_KEY: self.hass.data[DATA_ECOBEE_CONFIG].get(CONF_API_KEY)
|
|
}
|
|
)
|