diff --git a/homeassistant/helpers/service.py b/homeassistant/helpers/service.py index d954d7b9682..951f1c24402 100644 --- a/homeassistant/helpers/service.py +++ b/homeassistant/helpers/service.py @@ -633,16 +633,18 @@ async def async_get_all_descriptions( ints_or_excs = await async_get_integrations(hass, domains_with_missing_services) integrations: list[Integration] = [] for domain, int_or_exc in ints_or_excs.items(): - if type(int_or_exc) is Integration: # noqa: E721 + if type(int_or_exc) is Integration and int_or_exc.has_services: # noqa: E721 integrations.append(int_or_exc) continue if TYPE_CHECKING: assert isinstance(int_or_exc, Exception) _LOGGER.error("Failed to load integration: %s", domain, exc_info=int_or_exc) - contents = await hass.async_add_executor_job( - _load_services_files, hass, integrations - ) - loaded = dict(zip(domains_with_missing_services, contents)) + + if integrations: + contents = await hass.async_add_executor_job( + _load_services_files, hass, integrations + ) + loaded = dict(zip(domains_with_missing_services, contents)) # Load translations for all service domains translations = await translation.async_get_translations( diff --git a/homeassistant/loader.py b/homeassistant/loader.py index 71f746e322b..8e44ca38d77 100644 --- a/homeassistant/loader.py +++ b/homeassistant/loader.py @@ -838,6 +838,11 @@ class Integration: """Return if the integration has translations.""" return "translations" in self._top_level_files + @cached_property + def has_services(self) -> bool: + """Return if the integration has services.""" + return "services.yaml" in self._top_level_files + @property def mqtt(self) -> list[str] | None: """Return Integration MQTT entries.""" diff --git a/tests/helpers/test_service.py b/tests/helpers/test_service.py index b0d94bad23b..113c452e719 100644 --- a/tests/helpers/test_service.py +++ b/tests/helpers/test_service.py @@ -35,6 +35,7 @@ from homeassistant.helpers import ( template, ) import homeassistant.helpers.config_validation as cv +from homeassistant.loader import async_get_integration from homeassistant.setup import async_setup_component from tests.common import ( @@ -564,14 +565,29 @@ async def test_async_get_all_descriptions(hass: HomeAssistant) -> None: """Test async_get_all_descriptions.""" group = hass.components.group group_config = {group.DOMAIN: {}} - await async_setup_component(hass, group.DOMAIN, group_config) - descriptions = await service.async_get_all_descriptions(hass) + assert await async_setup_component(hass, group.DOMAIN, group_config) + assert await async_setup_component(hass, "system_health", {}) + + with patch( + "homeassistant.helpers.service._load_services_files", + side_effect=service._load_services_files, + ) as proxy_load_services_files: + descriptions = await service.async_get_all_descriptions(hass) + + # Test we only load services.yaml for integrations with services.yaml + # And system_health has no services + assert proxy_load_services_files.mock_calls[0][1][1] == [ + await async_get_integration(hass, "group") + ] assert len(descriptions) == 1 assert "description" in descriptions["group"]["reload"] assert "fields" in descriptions["group"]["reload"] + # Does not have services + assert "system_health" not in descriptions + logger = hass.components.logger logger_config = {logger.DOMAIN: {}} diff --git a/tests/test_loader.py b/tests/test_loader.py index b51a9a846b8..a2868976876 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -1685,3 +1685,11 @@ async def test_integration_warnings( """Test integration warnings.""" await loader.async_get_integration(hass, "test_package_loaded_loop") assert "configured to to import its code in the event loop" in caplog.text + + +async def test_has_services(hass: HomeAssistant, enable_custom_integrations) -> None: + """Test has_services.""" + integration = await loader.async_get_integration(hass, "test") + assert integration.has_services is False + integration = await loader.async_get_integration(hass, "test_with_services") + assert integration.has_services is True diff --git a/tests/testing_config/custom_components/test_with_services/__init__.py b/tests/testing_config/custom_components/test_with_services/__init__.py new file mode 100644 index 00000000000..e39053682e3 --- /dev/null +++ b/tests/testing_config/custom_components/test_with_services/__init__.py @@ -0,0 +1 @@ +"""Provide a mock integration.""" diff --git a/tests/testing_config/custom_components/test_with_services/manifest.json b/tests/testing_config/custom_components/test_with_services/manifest.json new file mode 100644 index 00000000000..f42d56e2e7d --- /dev/null +++ b/tests/testing_config/custom_components/test_with_services/manifest.json @@ -0,0 +1,4 @@ +{ + "domain": "test_with_services", + "version": "1.0" +} diff --git a/tests/testing_config/custom_components/test_with_services/services.yaml b/tests/testing_config/custom_components/test_with_services/services.yaml new file mode 100644 index 00000000000..e69de29bb2d