From 5ab527e59c774ef3a2263f487d7ddd22d01bc0ad Mon Sep 17 00:00:00 2001 From: Franck Nijhof Date: Tue, 28 Dec 2021 21:17:53 +0100 Subject: [PATCH] Add configuration flow to CPU Speed (#62929) --- .coveragerc | 1 + CODEOWNERS | 1 + homeassistant/components/cpuspeed/__init__.py | 17 ++++- .../components/cpuspeed/config_flow.py | 38 ++++++++++ homeassistant/components/cpuspeed/const.py | 10 +++ .../components/cpuspeed/manifest.json | 1 + homeassistant/components/cpuspeed/sensor.py | 29 ++++++- .../components/cpuspeed/strings.json | 14 ++++ .../components/cpuspeed/translations/en.json | 14 ++++ homeassistant/generated/config_flows.py | 1 + requirements_test_all.txt | 3 + script/hassfest/translations.py | 1 + tests/components/cpuspeed/__init__.py | 1 + tests/components/cpuspeed/conftest.py | 31 ++++++++ tests/components/cpuspeed/test_config_flow.py | 76 +++++++++++++++++++ 15 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 homeassistant/components/cpuspeed/config_flow.py create mode 100644 homeassistant/components/cpuspeed/const.py create mode 100644 homeassistant/components/cpuspeed/strings.json create mode 100644 homeassistant/components/cpuspeed/translations/en.json create mode 100644 tests/components/cpuspeed/__init__.py create mode 100644 tests/components/cpuspeed/conftest.py create mode 100644 tests/components/cpuspeed/test_config_flow.py diff --git a/.coveragerc b/.coveragerc index 636cf19baae..ecd80cb79a2 100644 --- a/.coveragerc +++ b/.coveragerc @@ -179,6 +179,7 @@ omit = homeassistant/components/coolmaster/climate.py homeassistant/components/coolmaster/const.py homeassistant/components/cppm_tracker/device_tracker.py + homeassistant/components/cpuspeed/__init__.py homeassistant/components/cpuspeed/sensor.py homeassistant/components/crownstone/__init__.py homeassistant/components/crownstone/const.py diff --git a/CODEOWNERS b/CODEOWNERS index 3fdeef44712..fb0c20f5bb9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -181,6 +181,7 @@ tests/components/counter/* @fabaff homeassistant/components/cover/* @home-assistant/core tests/components/cover/* @home-assistant/core homeassistant/components/cpuspeed/* @fabaff +tests/components/cpuspeed/* @fabaff homeassistant/components/crownstone/* @Crownstone @RicArch97 tests/components/crownstone/* @Crownstone @RicArch97 homeassistant/components/cups/* @fabaff diff --git a/homeassistant/components/cpuspeed/__init__.py b/homeassistant/components/cpuspeed/__init__.py index c6121a68835..1c1ebd5084f 100644 --- a/homeassistant/components/cpuspeed/__init__.py +++ b/homeassistant/components/cpuspeed/__init__.py @@ -1 +1,16 @@ -"""The cpuspeed component.""" +"""The CPU Speed integration.""" +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant + +from .const import PLATFORMS + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Set up from a config entry.""" + hass.config_entries.async_setup_platforms(entry, PLATFORMS) + return True + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Unload a config entry.""" + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/cpuspeed/config_flow.py b/homeassistant/components/cpuspeed/config_flow.py new file mode 100644 index 00000000000..2a6e2bdee8c --- /dev/null +++ b/homeassistant/components/cpuspeed/config_flow.py @@ -0,0 +1,38 @@ +"""Config flow to configure the CPU Speed integration.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.config_entries import ConfigFlow +from homeassistant.const import CONF_NAME +from homeassistant.data_entry_flow import FlowResult + +from .const import DOMAIN + + +class CPUSpeedFlowHandler(ConfigFlow, domain=DOMAIN): + """Config flow for CPU Speed.""" + + VERSION = 1 + + _imported_name: str | None = None + + async def async_step_user( + self, user_input: dict[str, Any] | None = None + ) -> FlowResult: + """Handle a flow initialized by the user.""" + await self.async_set_unique_id(DOMAIN) + self._abort_if_unique_id_configured() + + if user_input is None: + return self.async_show_form(step_id="user") + + return self.async_create_entry( + title=self._imported_name or "CPU Speed", + data={}, + ) + + async def async_step_import(self, config: dict[str, Any]) -> FlowResult: + """Handle a flow initialized by importing a config.""" + self._imported_name = config.get(CONF_NAME) + return await self.async_step_user(user_input={}) diff --git a/homeassistant/components/cpuspeed/const.py b/homeassistant/components/cpuspeed/const.py new file mode 100644 index 00000000000..8fdb8d3c986 --- /dev/null +++ b/homeassistant/components/cpuspeed/const.py @@ -0,0 +1,10 @@ +"""Constants for the CPU Speed integration.""" +import logging +from typing import Final + +from homeassistant.const import Platform + +DOMAIN: Final = "cpuspeed" +PLATFORMS = [Platform.SENSOR] + +LOGGER = logging.getLogger(__package__) diff --git a/homeassistant/components/cpuspeed/manifest.json b/homeassistant/components/cpuspeed/manifest.json index 99c93a6d610..1426db00759 100644 --- a/homeassistant/components/cpuspeed/manifest.json +++ b/homeassistant/components/cpuspeed/manifest.json @@ -3,6 +3,7 @@ "name": "CPU Speed", "documentation": "https://www.home-assistant.io/integrations/cpuspeed", "requirements": ["py-cpuinfo==8.0.0"], + "config_flow": true, "codeowners": ["@fabaff"], "iot_class": "local_push" } diff --git a/homeassistant/components/cpuspeed/sensor.py b/homeassistant/components/cpuspeed/sensor.py index e832d35fbd8..e8e8ea13990 100644 --- a/homeassistant/components/cpuspeed/sensor.py +++ b/homeassistant/components/cpuspeed/sensor.py @@ -7,12 +7,15 @@ from cpuinfo import cpuinfo import voluptuous as vol from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import CONF_NAME, FREQUENCY_GIGAHERTZ from homeassistant.core import HomeAssistant import homeassistant.helpers.config_validation as cv from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType +from .const import DOMAIN, LOGGER + ATTR_BRAND = "brand" ATTR_HZ = "ghz_advertised" ATTR_ARCH = "arch" @@ -34,11 +37,31 @@ async def async_setup_platform( discovery_info: DiscoveryInfoType | None = None, ) -> None: """Set up the CPU speed sensor.""" - name = config[CONF_NAME] - async_add_entities([CpuSpeedSensor(name)], True) + LOGGER.warning( + "Configuration of the CPU Speed platform in YAML is deprecated and will be " + "removed in Home Assistant 2022.4; Your existing configuration " + "has been imported into the UI automatically and can be safely removed " + "from your configuration.yaml file" + ) + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_NAME: config[CONF_NAME]}, + ) + ) -class CpuSpeedSensor(SensorEntity): +async def async_setup_entry( + hass: HomeAssistant, + entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up the platform from config_entry.""" + async_add_entities([CPUSpeedSensor("CPU Speed")], True) + + +class CPUSpeedSensor(SensorEntity): """Representation of a CPU sensor.""" _attr_native_unit_of_measurement = FREQUENCY_GIGAHERTZ diff --git a/homeassistant/components/cpuspeed/strings.json b/homeassistant/components/cpuspeed/strings.json new file mode 100644 index 00000000000..948aa9f647c --- /dev/null +++ b/homeassistant/components/cpuspeed/strings.json @@ -0,0 +1,14 @@ +{ + "title": "CPU Speed", + "config": { + "step": { + "user": { + "title": "CPU Speed", + "description": "[%key:common::config_flow::description::confirm_setup%]" + } + }, + "abort": { + "alread_configured": "[%key:common::config_flow::abort::single_instance_allowed%]" + } + } +} diff --git a/homeassistant/components/cpuspeed/translations/en.json b/homeassistant/components/cpuspeed/translations/en.json new file mode 100644 index 00000000000..53536fc663c --- /dev/null +++ b/homeassistant/components/cpuspeed/translations/en.json @@ -0,0 +1,14 @@ +{ + "config": { + "abort": { + "alread_configured": "Already configured. Only a single configuration possible." + }, + "step": { + "user": { + "description": "Do you want to start set up?", + "title": "CPU Speed" + } + } + }, + "title": "CPU Speed" +} \ No newline at end of file diff --git a/homeassistant/generated/config_flows.py b/homeassistant/generated/config_flows.py index 32871b581d6..c8888adbbd5 100644 --- a/homeassistant/generated/config_flows.py +++ b/homeassistant/generated/config_flows.py @@ -60,6 +60,7 @@ FLOWS = [ "control4", "coolmaster", "coronavirus", + "cpuspeed", "crownstone", "daikin", "deconz", diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 2a38fbc96cc..212d3235390 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -795,6 +795,9 @@ pvo==0.2.0 # homeassistant.components.canary py-canary==0.5.1 +# homeassistant.components.cpuspeed +py-cpuinfo==8.0.0 + # homeassistant.components.melissa py-melissa-climate==2.1.4 diff --git a/script/hassfest/translations.py b/script/hassfest/translations.py index 841638a9718..d174f238217 100644 --- a/script/hassfest/translations.py +++ b/script/hassfest/translations.py @@ -24,6 +24,7 @@ RE_REFERENCE = r"\[\%key:(.+)\%\]" # Only allow translatino of integration names if they contain non-brand names ALLOW_NAME_TRANSLATION = { "cert_expiry", + "cpuspeed", "emulated_roku", "garages_amsterdam", "google_travel_time", diff --git a/tests/components/cpuspeed/__init__.py b/tests/components/cpuspeed/__init__.py new file mode 100644 index 00000000000..b65adbc70eb --- /dev/null +++ b/tests/components/cpuspeed/__init__.py @@ -0,0 +1 @@ +"""Tests for the CPU Speed integration.""" diff --git a/tests/components/cpuspeed/conftest.py b/tests/components/cpuspeed/conftest.py new file mode 100644 index 00000000000..1a221e8a08b --- /dev/null +++ b/tests/components/cpuspeed/conftest.py @@ -0,0 +1,31 @@ +"""Fixtures for CPU Speed integration tests.""" +from __future__ import annotations + +from collections.abc import Generator +from unittest.mock import AsyncMock, patch + +import pytest + +from homeassistant.components.cpuspeed.const import DOMAIN + +from tests.common import MockConfigEntry + + +@pytest.fixture +def mock_config_entry() -> MockConfigEntry: + """Return the default mocked config entry.""" + return MockConfigEntry( + title="CPU Speed", + domain=DOMAIN, + data={}, + unique_id=DOMAIN, + ) + + +@pytest.fixture +def mock_setup_entry() -> Generator[AsyncMock, None, None]: + """Mock setting up a config entry.""" + with patch( + "homeassistant.components.cpuspeed.async_setup_entry", return_value=True + ) as mock_setup: + yield mock_setup diff --git a/tests/components/cpuspeed/test_config_flow.py b/tests/components/cpuspeed/test_config_flow.py new file mode 100644 index 00000000000..f2ffdebfe16 --- /dev/null +++ b/tests/components/cpuspeed/test_config_flow.py @@ -0,0 +1,76 @@ +"""Tests for the CPU Speed config flow.""" + +from unittest.mock import AsyncMock + +from homeassistant.components.cpuspeed.const import DOMAIN +from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER +from homeassistant.const import CONF_NAME +from homeassistant.core import HomeAssistant +from homeassistant.data_entry_flow import ( + RESULT_TYPE_ABORT, + RESULT_TYPE_CREATE_ENTRY, + RESULT_TYPE_FORM, +) + +from tests.common import MockConfigEntry + + +async def test_full_user_flow( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, +) -> None: + """Test the full user configuration flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + + assert result.get("type") == RESULT_TYPE_FORM + assert result.get("step_id") == SOURCE_USER + assert "flow_id" in result + + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={}, + ) + + assert result2.get("type") == RESULT_TYPE_CREATE_ENTRY + assert result2.get("title") == "CPU Speed" + assert result2.get("data") == {} + + assert len(mock_setup_entry.mock_calls) == 1 + + +async def test_already_configured( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, + mock_config_entry: MockConfigEntry, +) -> None: + """Test we abort if already configured.""" + mock_config_entry.add_to_hass(hass) + + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + + assert result.get("type") == RESULT_TYPE_ABORT + assert result.get("reason") == "already_configured" + + assert len(mock_setup_entry.mock_calls) == 0 + + +async def test_import_flow( + hass: HomeAssistant, + mock_setup_entry: AsyncMock, +) -> None: + """Test the import configuration flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, + context={"source": SOURCE_IMPORT}, + data={CONF_NAME: "Frenck's CPU"}, + ) + + assert result.get("type") == RESULT_TYPE_CREATE_ENTRY + assert result.get("title") == "Frenck's CPU" + assert result.get("data") == {} + + assert len(mock_setup_entry.mock_calls) == 1