From fd53e116bb2f1a62a00615cb5bb509c6401992d1 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Wed, 27 Sep 2023 10:11:42 +0200 Subject: [PATCH] Add config flow to color extractor (#100862) --- .../components/color_extractor/__init__.py | 17 ++++- .../components/color_extractor/config_flow.py | 64 +++++++++++++++++ .../components/color_extractor/const.py | 1 + .../components/color_extractor/manifest.json | 2 +- .../components/color_extractor/strings.json | 16 +++++ homeassistant/generated/config_flows.py | 1 + homeassistant/generated/integrations.json | 2 +- .../color_extractor/test_config_flow.py | 70 +++++++++++++++++++ .../color_extractor/test_service.py | 2 +- 9 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 homeassistant/components/color_extractor/config_flow.py create mode 100644 tests/components/color_extractor/test_config_flow.py diff --git a/homeassistant/components/color_extractor/__init__.py b/homeassistant/components/color_extractor/__init__.py index fb04ebb76a4..af460f819cd 100644 --- a/homeassistant/components/color_extractor/__init__.py +++ b/homeassistant/components/color_extractor/__init__.py @@ -13,6 +13,7 @@ from homeassistant.components.light import ( DOMAIN as LIGHT_DOMAIN, LIGHT_TURN_ON_SCHEMA, ) +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import SERVICE_TURN_ON as LIGHT_SERVICE_TURN_ON from homeassistant.core import HomeAssistant, ServiceCall from homeassistant.helpers import aiohttp_client @@ -58,8 +59,20 @@ def _get_color(file_handler) -> tuple: return color -async def async_setup(hass: HomeAssistant, hass_config: ConfigType) -> bool: - """Set up services for color_extractor integration.""" +async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: + """Set up the Color extractor component.""" + + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_IMPORT}, data={} + ) + ) + + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Load a config entry.""" async def async_handle_service(service_call: ServiceCall) -> None: """Decide which color_extractor method to call based on service.""" diff --git a/homeassistant/components/color_extractor/config_flow.py b/homeassistant/components/color_extractor/config_flow.py new file mode 100644 index 00000000000..32b803d14f9 --- /dev/null +++ b/homeassistant/components/color_extractor/config_flow.py @@ -0,0 +1,64 @@ +"""Config flow to configure the Color extractor integration.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.config_entries import ConfigFlow +from homeassistant.core import DOMAIN as HOMEASSISTANT_DOMAIN +from homeassistant.data_entry_flow import FlowResult, FlowResultType +from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue + +from .const import DEFAULT_NAME, DOMAIN + + +class ColorExtractorConfigFlow(ConfigFlow, domain=DOMAIN): + """Config flow for Color extractor.""" + + VERSION = 1 + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle a flow initialized by the user.""" + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + + if user_input is not None: + return self.async_create_entry(title=DEFAULT_NAME, data={}) + + return self.async_show_form(step_id="user") + + async def async_step_import(self, user_input: dict[str, Any]) -> FlowResult: + """Handle import from configuration.yaml.""" + result = await self.async_step_user(user_input) + if result["type"] == FlowResultType.CREATE_ENTRY: + async_create_issue( + self.hass, + HOMEASSISTANT_DOMAIN, + f"deprecated_yaml_{DOMAIN}", + breaks_in_ha_version="2024.4.0", + is_fixable=False, + issue_domain=DOMAIN, + severity=IssueSeverity.WARNING, + translation_key="deprecated_yaml", + translation_placeholders={ + "domain": DOMAIN, + "integration_title": "Color extractor", + }, + ) + else: + async_create_issue( + self.hass, + DOMAIN, + "deprecated_yaml", + breaks_in_ha_version="2024.4.0", + is_fixable=False, + issue_domain=DOMAIN, + severity=IssueSeverity.WARNING, + translation_key="deprecated_yaml", + translation_placeholders={ + "domain": DOMAIN, + "integration_title": "Color extractor", + }, + ) + return result diff --git a/homeassistant/components/color_extractor/const.py b/homeassistant/components/color_extractor/const.py index a6c59ea434b..e783dcb533d 100644 --- a/homeassistant/components/color_extractor/const.py +++ b/homeassistant/components/color_extractor/const.py @@ -3,5 +3,6 @@ ATTR_PATH = "color_extract_path" ATTR_URL = "color_extract_url" DOMAIN = "color_extractor" +DEFAULT_NAME = "Color extractor" SERVICE_TURN_ON = "turn_on" diff --git a/homeassistant/components/color_extractor/manifest.json b/homeassistant/components/color_extractor/manifest.json index 07e9b43a5e5..c87ac2540a6 100644 --- a/homeassistant/components/color_extractor/manifest.json +++ b/homeassistant/components/color_extractor/manifest.json @@ -2,7 +2,7 @@ "domain": "color_extractor", "name": "ColorExtractor", "codeowners": ["@GenericStudent"], - "config_flow": false, + "config_flow": true, "documentation": "https://www.home-assistant.io/integrations/color_extractor", "requirements": ["colorthief==0.2.1"] } diff --git a/homeassistant/components/color_extractor/strings.json b/homeassistant/components/color_extractor/strings.json index 3dc02f56030..aa5fd5f4ef7 100644 --- a/homeassistant/components/color_extractor/strings.json +++ b/homeassistant/components/color_extractor/strings.json @@ -1,4 +1,20 @@ { + "config": { + "step": { + "user": { + "description": "[%key:common::config_flow::description::confirm_setup%]" + } + }, + "abort": { + "single_instance_allowed": "[%key:common::config_flow::abort::single_instance_allowed%]" + } + }, + "issues": { + "deprecated_yaml": { + "title": "The {integration_title} YAML configuration is being removed", + "description": "Configuring {integration_title} using YAML is being removed.\n\nYour configuration is already imported.\n\nRemove the `{domain}` configuration from your configuration.yaml file and restart Home Assistant to fix this issue." + } + }, "services": { "turn_on": { "name": "[%key:common::action::turn_on%]", diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index acf324c107f..c3ee346664a 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -83,6 +83,7 @@ FLOWS = { "cloudflare", "co2signal", "coinbase", + "color_extractor", "comelit", "control4", "coolmaster", diff --git a/homeassistant/generated/integrations.json b/homeassistant/generated/integrations.json index 394bfa4f391..bc759ec1ae6 100644 --- a/homeassistant/generated/integrations.json +++ b/homeassistant/generated/integrations.json @@ -876,7 +876,7 @@ "color_extractor": { "name": "ColorExtractor", "integration_type": "hub", - "config_flow": false + "config_flow": true }, "comed": { "name": "Commonwealth Edison (ComEd)", diff --git a/tests/components/color_extractor/test_config_flow.py b/tests/components/color_extractor/test_config_flow.py new file mode 100644 index 00000000000..9dc928da73f --- /dev/null +++ b/tests/components/color_extractor/test_config_flow.py @@ -0,0 +1,70 @@ +"""Tests for the Color extractor config flow.""" +from unittest.mock import patch + +import pytest + +from homeassistant.components.color_extractor.const import DOMAIN +from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import FlowResultType + +from tests.common import MockConfigEntry + + +async def test_full_user_flow(hass: HomeAssistant) -> None: + """Test the full user configuration flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + + assert result.get("type") == FlowResultType.FORM + assert result.get("step_id") == "user" + + with patch( + "homeassistant.components.color_extractor.async_setup_entry", + return_value=True, + ) as mock_setup_entry: + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={}, + ) + + assert result.get("type") == FlowResultType.CREATE_ENTRY + assert result.get("title") == "Color extractor" + assert result.get("data") == {} + assert result.get("options") == {} + assert len(mock_setup_entry.mock_calls) == 1 + + +@pytest.mark.parametrize("source", [SOURCE_USER, SOURCE_IMPORT]) +async def test_single_instance_allowed( + hass: HomeAssistant, + source: str, +) -> None: + """Test we abort if already setup.""" + mock_config_entry = MockConfigEntry(domain=DOMAIN) + + mock_config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": source}, data={} + ) + + assert result.get("type") == FlowResultType.ABORT + assert result.get("reason") == "single_instance_allowed" + + +async def test_import_flow( + hass: HomeAssistant, +) -> None: + """Test the import configuration flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={}, + ) + + assert result.get("type") == FlowResultType.CREATE_ENTRY + assert result.get("title") == "Color extractor" + assert result.get("data") == {} + assert result.get("options") == {} diff --git a/tests/components/color_extractor/test_service.py b/tests/components/color_extractor/test_service.py index 31218387858..ae3e799e9d2 100644 --- a/tests/components/color_extractor/test_service.py +++ b/tests/components/color_extractor/test_service.py @@ -63,7 +63,7 @@ def _close_enough(actual_rgb, testing_rgb): @pytest.fixture(autouse=True) -async def setup_light(hass): +async def setup_light(hass: HomeAssistant): """Configure our light component to work against for testing.""" assert await async_setup_component( hass, LIGHT_DOMAIN, {LIGHT_DOMAIN: {"platform": "demo"}}