Cleanup ecobee YAML configuration import (#136633)

This commit is contained in:
epenet 2025-01-28 14:04:09 +01:00 committed by GitHub
parent c2da844f76
commit 3dbcdf933e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 8 additions and 215 deletions

View File

@ -3,57 +3,19 @@
from datetime import timedelta
from pyecobee import ECOBEE_API_KEY, ECOBEE_REFRESH_TOKEN, Ecobee, ExpiredTokenError
import voluptuous as vol
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import ConfigType
from homeassistant.util import Throttle
from .const import (
_LOGGER,
CONF_REFRESH_TOKEN,
DATA_ECOBEE_CONFIG,
DATA_HASS_CONFIG,
DOMAIN,
PLATFORMS,
)
from .const import _LOGGER, CONF_REFRESH_TOKEN, PLATFORMS
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
)
type EcobeeConfigEntry = ConfigEntry[EcobeeData]
async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
"""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 integration. Otherwise, the user will have to
continue setting up the integration via the config flow.
"""
hass.data[DATA_ECOBEE_CONFIG] = config.get(DOMAIN, {})
hass.data[DATA_HASS_CONFIG] = config
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: HomeAssistant, entry: EcobeeConfigEntry) -> bool:
"""Set up ecobee via a config entry."""
api_key = entry.data[CONF_API_KEY]

View File

@ -2,20 +2,15 @@
from typing import Any
from pyecobee import (
ECOBEE_API_KEY,
ECOBEE_CONFIG_FILENAME,
ECOBEE_REFRESH_TOKEN,
Ecobee,
)
from pyecobee import ECOBEE_API_KEY, Ecobee
import voluptuous as vol
from homeassistant.config_entries import ConfigFlow, ConfigFlowResult
from homeassistant.const import CONF_API_KEY
from homeassistant.exceptions import HomeAssistantError
from homeassistant.util.json import load_json_object
from .const import _LOGGER, CONF_REFRESH_TOKEN, DATA_ECOBEE_CONFIG, DOMAIN
from .const import CONF_REFRESH_TOKEN, DOMAIN
_USER_SCHEMA = vol.Schema({vol.Required(CONF_API_KEY): str})
class EcobeeFlowHandler(ConfigFlow, domain=DOMAIN):
@ -30,11 +25,6 @@ class EcobeeFlowHandler(ConfigFlow, domain=DOMAIN):
) -> ConfigFlowResult:
"""Handle a flow initiated by the user."""
errors = {}
stored_api_key = (
self.hass.data[DATA_ECOBEE_CONFIG].get(CONF_API_KEY)
if DATA_ECOBEE_CONFIG in self.hass.data
else ""
)
if user_input is not None:
# Use the user-supplied API key to attempt to obtain a PIN from ecobee.
@ -47,9 +37,7 @@ class EcobeeFlowHandler(ConfigFlow, domain=DOMAIN):
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
{vol.Required(CONF_API_KEY, default=stored_api_key): str}
),
data_schema=_USER_SCHEMA,
errors=errors,
)
@ -75,50 +63,3 @@ class EcobeeFlowHandler(ConfigFlow, domain=DOMAIN):
errors=errors,
description_placeholders={"pin": self._ecobee.pin},
)
async def async_step_import(self, import_data: None) -> ConfigFlowResult:
"""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_object, 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 configuration"
" 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)
}
)

View File

@ -20,8 +20,6 @@ from homeassistant.const import Platform
_LOGGER = logging.getLogger(__package__)
DOMAIN = "ecobee"
DATA_ECOBEE_CONFIG = "ecobee_config"
DATA_HASS_CONFIG = "ecobee_hass_config"
ATTR_CONFIG_ENTRY_ID = "entry_id"
ATTR_AVAILABLE_SENSORS = "available_sensors"
ATTR_ACTIVE_SENSORS = "active_sensors"

View File

@ -2,15 +2,8 @@
from unittest.mock import patch
from pyecobee import ECOBEE_API_KEY, ECOBEE_REFRESH_TOKEN
import pytest
from homeassistant.components.ecobee import config_flow
from homeassistant.components.ecobee.const import (
CONF_REFRESH_TOKEN,
DATA_ECOBEE_CONFIG,
DOMAIN,
)
from homeassistant.components.ecobee.const import CONF_REFRESH_TOKEN, DOMAIN
from homeassistant.config_entries import SOURCE_USER
from homeassistant.const import CONF_API_KEY
from homeassistant.core import HomeAssistant
@ -35,7 +28,6 @@ async def test_user_step_without_user_input(hass: HomeAssistant) -> None:
"""Test expected result if user step is called."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {}
result = await flow.async_step_user()
assert result["type"] is FlowResultType.FORM
@ -46,7 +38,6 @@ async def test_pin_request_succeeds(hass: HomeAssistant) -> None:
"""Test expected result if pin request succeeds."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {}
with patch("homeassistant.components.ecobee.config_flow.Ecobee") as mock_ecobee:
mock_ecobee = mock_ecobee.return_value
@ -64,7 +55,6 @@ async def test_pin_request_fails(hass: HomeAssistant) -> None:
"""Test expected result if pin request fails."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {}
with patch("homeassistant.components.ecobee.config_flow.Ecobee") as mock_ecobee:
mock_ecobee = mock_ecobee.return_value
@ -81,7 +71,6 @@ async def test_token_request_succeeds(hass: HomeAssistant) -> None:
"""Test expected result if token request succeeds."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {}
with patch("homeassistant.components.ecobee.config_flow.Ecobee") as mock_ecobee:
mock_ecobee = mock_ecobee.return_value
@ -105,7 +94,6 @@ async def test_token_request_fails(hass: HomeAssistant) -> None:
"""Test expected result if token request fails."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {}
with patch("homeassistant.components.ecobee.config_flow.Ecobee") as mock_ecobee:
mock_ecobee = mock_ecobee.return_value
@ -120,99 +108,3 @@ async def test_token_request_fails(hass: HomeAssistant) -> None:
assert result["step_id"] == "authorize"
assert result["errors"]["base"] == "token_request_failed"
assert result["description_placeholders"] == {"pin": "test-pin"}
@pytest.mark.skip(reason="Flaky/slow")
async def test_import_flow_triggered_but_no_ecobee_conf(hass: HomeAssistant) -> None:
"""Test expected result if import flow triggers but ecobee.conf doesn't exist."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {}
result = await flow.async_step_import(import_data=None)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "user"
async def test_import_flow_triggered_with_ecobee_conf_and_valid_data_and_valid_tokens(
hass: HomeAssistant,
) -> None:
"""Test expected result if import flow triggers and ecobee.conf exists with valid tokens."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
MOCK_ECOBEE_CONF = {ECOBEE_API_KEY: None, ECOBEE_REFRESH_TOKEN: None}
with (
patch(
"homeassistant.components.ecobee.config_flow.load_json_object",
return_value=MOCK_ECOBEE_CONF,
),
patch("homeassistant.components.ecobee.config_flow.Ecobee") as mock_ecobee,
):
mock_ecobee = mock_ecobee.return_value
mock_ecobee.refresh_tokens.return_value = True
mock_ecobee.api_key = "test-api-key"
mock_ecobee.refresh_token = "test-token"
result = await flow.async_step_import(import_data=None)
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == DOMAIN
assert result["data"] == {
CONF_API_KEY: "test-api-key",
CONF_REFRESH_TOKEN: "test-token",
}
async def test_import_flow_triggered_with_ecobee_conf_and_invalid_data(
hass: HomeAssistant,
) -> None:
"""Test expected result if import flow triggers and ecobee.conf exists with invalid data."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {CONF_API_KEY: "test-api-key"}
MOCK_ECOBEE_CONF = {}
with (
patch(
"homeassistant.components.ecobee.config_flow.load_json_object",
return_value=MOCK_ECOBEE_CONF,
),
patch.object(flow, "async_step_user") as mock_async_step_user,
):
await flow.async_step_import(import_data=None)
mock_async_step_user.assert_called_once_with(
user_input={CONF_API_KEY: "test-api-key"}
)
async def test_import_flow_triggered_with_ecobee_conf_and_valid_data_and_stale_tokens(
hass: HomeAssistant,
) -> None:
"""Test expected result if import flow triggers and ecobee.conf exists with stale tokens."""
flow = config_flow.EcobeeFlowHandler()
flow.hass = hass
flow.hass.data[DATA_ECOBEE_CONFIG] = {CONF_API_KEY: "test-api-key"}
MOCK_ECOBEE_CONF = {ECOBEE_API_KEY: None, ECOBEE_REFRESH_TOKEN: None}
with (
patch(
"homeassistant.components.ecobee.config_flow.load_json_object",
return_value=MOCK_ECOBEE_CONF,
),
patch("homeassistant.components.ecobee.config_flow.Ecobee") as mock_ecobee,
patch.object(flow, "async_step_user") as mock_async_step_user,
):
mock_ecobee = mock_ecobee.return_value
mock_ecobee.refresh_tokens.return_value = False
await flow.async_step_import(import_data=None)
mock_async_step_user.assert_called_once_with(
user_input={CONF_API_KEY: "test-api-key"}
)