diff --git a/homeassistant/components/cpuspeed/__init__.py b/homeassistant/components/cpuspeed/__init__.py index 1c1ebd5084f..a5d6a35459f 100644 --- a/homeassistant/components/cpuspeed/__init__.py +++ b/homeassistant/components/cpuspeed/__init__.py @@ -1,12 +1,21 @@ """The CPU Speed integration.""" +from cpuinfo import cpuinfo + from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant -from .const import PLATFORMS +from .const import LOGGER, PLATFORMS async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up from a config entry.""" + if not await hass.async_add_executor_job(cpuinfo.get_cpu_info): + LOGGER.error( + "Unable to get CPU information, the CPU Speed integration " + "is not compatible with your system" + ) + return False + hass.config_entries.async_setup_platforms(entry, PLATFORMS) return True diff --git a/homeassistant/components/cpuspeed/config_flow.py b/homeassistant/components/cpuspeed/config_flow.py index 2a6e2bdee8c..4b3c39f148a 100644 --- a/homeassistant/components/cpuspeed/config_flow.py +++ b/homeassistant/components/cpuspeed/config_flow.py @@ -3,6 +3,8 @@ from __future__ import annotations from typing import Any +from cpuinfo import cpuinfo + from homeassistant.config_entries import ConfigFlow from homeassistant.const import CONF_NAME from homeassistant.data_entry_flow import FlowResult @@ -27,6 +29,9 @@ class CPUSpeedFlowHandler(ConfigFlow, domain=DOMAIN): if user_input is None: return self.async_show_form(step_id="user") + if not await self.hass.async_add_executor_job(cpuinfo.get_cpu_info): + return self.async_abort(reason="not_compatible") + return self.async_create_entry( title=self._imported_name or "CPU Speed", data={}, diff --git a/homeassistant/components/cpuspeed/strings.json b/homeassistant/components/cpuspeed/strings.json index 948aa9f647c..932ba29e555 100644 --- a/homeassistant/components/cpuspeed/strings.json +++ b/homeassistant/components/cpuspeed/strings.json @@ -8,7 +8,8 @@ } }, "abort": { - "alread_configured": "[%key:common::config_flow::abort::single_instance_allowed%]" + "alread_configured": "[%key:common::config_flow::abort::single_instance_allowed%]", + "not_compatible": "Unable to get CPU information, this integration is not compatible with your system" } } } diff --git a/homeassistant/components/cpuspeed/translations/en.json b/homeassistant/components/cpuspeed/translations/en.json index 53536fc663c..4d4205d54e7 100644 --- a/homeassistant/components/cpuspeed/translations/en.json +++ b/homeassistant/components/cpuspeed/translations/en.json @@ -1,7 +1,8 @@ { "config": { "abort": { - "alread_configured": "Already configured. Only a single configuration possible." + "alread_configured": "Already configured. Only a single configuration possible.", + "not_compatible": "Unable to get CPU information, this integration is not compatible with your system" }, "step": { "user": { diff --git a/tests/components/cpuspeed/conftest.py b/tests/components/cpuspeed/conftest.py index 1a221e8a08b..97eb668b02e 100644 --- a/tests/components/cpuspeed/conftest.py +++ b/tests/components/cpuspeed/conftest.py @@ -2,7 +2,7 @@ from __future__ import annotations from collections.abc import Generator -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -22,6 +22,20 @@ def mock_config_entry() -> MockConfigEntry: ) +@pytest.fixture +def mock_cpuinfo_config_flow() -> Generator[MagicMock, None, None]: + """Return a mocked get_cpu_info. + + It is only used to check thruthy or falsy values, so it is mocked + to return True. + """ + with patch( + "homeassistant.components.cpuspeed.config_flow.cpuinfo.get_cpu_info", + return_value=True, + ) as cpuinfo_mock: + yield cpuinfo_mock + + @pytest.fixture def mock_setup_entry() -> Generator[AsyncMock, None, None]: """Mock setting up a config entry.""" diff --git a/tests/components/cpuspeed/test_config_flow.py b/tests/components/cpuspeed/test_config_flow.py index f2ffdebfe16..14563c82bff 100644 --- a/tests/components/cpuspeed/test_config_flow.py +++ b/tests/components/cpuspeed/test_config_flow.py @@ -1,6 +1,6 @@ """Tests for the CPU Speed config flow.""" -from unittest.mock import AsyncMock +from unittest.mock import AsyncMock, MagicMock from homeassistant.components.cpuspeed.const import DOMAIN from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER @@ -17,6 +17,7 @@ from tests.common import MockConfigEntry async def test_full_user_flow( hass: HomeAssistant, + mock_cpuinfo_config_flow: MagicMock, mock_setup_entry: AsyncMock, ) -> None: """Test the full user configuration flow.""" @@ -38,10 +39,12 @@ async def test_full_user_flow( assert result2.get("data") == {} assert len(mock_setup_entry.mock_calls) == 1 + assert len(mock_cpuinfo_config_flow.mock_calls) == 1 async def test_already_configured( hass: HomeAssistant, + mock_cpuinfo_config_flow: MagicMock, mock_setup_entry: AsyncMock, mock_config_entry: MockConfigEntry, ) -> None: @@ -56,10 +59,12 @@ async def test_already_configured( assert result.get("reason") == "already_configured" assert len(mock_setup_entry.mock_calls) == 0 + assert len(mock_cpuinfo_config_flow.mock_calls) == 0 async def test_import_flow( hass: HomeAssistant, + mock_cpuinfo_config_flow: MagicMock, mock_setup_entry: AsyncMock, ) -> None: """Test the import configuration flow.""" @@ -74,3 +79,31 @@ async def test_import_flow( assert result.get("data") == {} assert len(mock_setup_entry.mock_calls) == 1 + assert len(mock_cpuinfo_config_flow.mock_calls) == 1 + + +async def test_not_compatible( + hass: HomeAssistant, + mock_cpuinfo_config_flow: MagicMock, + mock_setup_entry: AsyncMock, +) -> None: + """Test we abort the configuration flow when incompatible.""" + 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 + + mock_cpuinfo_config_flow.return_value = {} + result2 = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={}, + ) + + assert result2.get("type") == RESULT_TYPE_ABORT + assert result2.get("reason") == "not_compatible" + + assert len(mock_setup_entry.mock_calls) == 0 + assert len(mock_cpuinfo_config_flow.mock_calls) == 1