From 6e2df0d26f7bc992293a8cf99112b058b37b7842 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 5 Oct 2026 15:23:33 +0200 Subject: [PATCH] Move MQTT config check and issue cleanup helpers to util (#184322) Co-authored-by: Claude Opus 5 --- homeassistant/components/mqtt/__init__.py | 48 +++-------------------- homeassistant/components/mqtt/util.py | 47 +++++++++++++++++++++- 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/homeassistant/components/mqtt/__init__.py b/homeassistant/components/mqtt/__init__.py index 60bdca8b635c..acec4613d4f9 100644 --- a/homeassistant/components/mqtt/__init__.py +++ b/homeassistant/components/mqtt/__init__.py @@ -37,7 +37,7 @@ from homeassistant.helpers.issue_registry import IssueSeverity, async_create_iss from homeassistant.helpers.reload import async_integration_yaml_config from homeassistant.helpers.service import async_register_admin_service from homeassistant.helpers.typing import ConfigType -from homeassistant.loader import async_get_integration, async_get_loaded_integration +from homeassistant.loader import async_get_loaded_integration from homeassistant.setup import SetupPhases, async_pause_setup from homeassistant.util.async_ import create_eager_task @@ -111,8 +111,10 @@ from .subscription import ( async_unsubscribe_topics, ) from .util import ( + async_check_config_schema, async_create_certificate_temp_files, async_forward_entry_setup_and_setup_discovery, + async_remove_mqtt_issues, async_wait_for_mqtt_client, mqtt_config_entry_enabled, platforms_from_config, @@ -262,46 +264,6 @@ async def _async_config_entry_updated(hass: HomeAssistant, entry: ConfigEntry) - hass.config_entries.async_schedule_reload(entry.entry_id) -@callback -def _async_remove_mqtt_issues(hass: HomeAssistant, mqtt_data: MqttData) -> None: - """Unregister open config issues.""" - issue_registry = ir.async_get(hass) - open_issues = [ - issue_id - for (domain, issue_id), issue_entry in issue_registry.issues.items() - if domain == DOMAIN and issue_entry.translation_key == "invalid_platform_config" - ] - for issue in open_issues: - ir.async_delete_issue(hass, DOMAIN, issue) - - -async def async_check_config_schema( - hass: HomeAssistant, config_yaml: ConfigType -) -> None: - """Validate manually configured MQTT items.""" - mqtt_data = hass.data[DATA_MQTT] - mqtt_config: list[dict[str, list[ConfigType]]] = config_yaml.get(DOMAIN, {}) - for mqtt_config_item in mqtt_config: - for domain, config_items in mqtt_config_item.items(): - schema = mqtt_data.reload_schema[domain] - for config in config_items: - try: - schema(config) - except probatio.Invalid as exc: - integration = await async_get_integration(hass, DOMAIN) - message = conf_util.format_schema_error( - hass, exc, domain, config, integration.documentation - ) - raise ServiceValidationError( - translation_domain=DOMAIN, - translation_key="invalid_platform_config_message", - translation_placeholders={ - "domain": domain, - "message": message, - }, - ) from exc - - def _platforms_in_use(hass: HomeAssistant, entry: ConfigEntry) -> set[str | Platform]: """Return a set of platforms in use.""" domains: set[str | Platform] = { @@ -439,7 +401,7 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: await async_check_config_schema(hass, config_yaml) # Remove repair issues - _async_remove_mqtt_issues(hass, mqtt_data) + async_remove_mqtt_issues(hass, mqtt_data) mqtt_data.config = new_config @@ -734,6 +696,6 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: mqtt_data.subscriptions_to_restore = subscriptions # Remove repair issues - _async_remove_mqtt_issues(hass, mqtt_data) + async_remove_mqtt_issues(hass, mqtt_data) return True diff --git a/homeassistant/components/mqtt/util.py b/homeassistant/components/mqtt/util.py index 64733329e0f4..9767eca8d1ce 100644 --- a/homeassistant/components/mqtt/util.py +++ b/homeassistant/components/mqtt/util.py @@ -11,6 +11,7 @@ from typing import Any import probatio +from homeassistant import config as conf_util from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import ( MAX_LENGTH_STATE_STATE, @@ -19,15 +20,17 @@ from homeassistant.const import ( Platform, ) from homeassistant.core import HomeAssistant, callback -from homeassistant.exceptions import HomeAssistantError +from homeassistant.exceptions import HomeAssistantError, ServiceValidationError from homeassistant.helpers import ( config_validation as cv, device_registry as dr, entity_registry as er, + issue_registry as ir, template, ) from homeassistant.helpers.entity import ENTITY_CATEGORIES_SCHEMA from homeassistant.helpers.typing import ConfigType +from homeassistant.loader import async_get_integration from homeassistant.util.async_ import create_eager_task from .const import ( @@ -43,7 +46,7 @@ from .const import ( DEFAULT_RETAIN, DOMAIN, ) -from .models import DATA_MQTT, DATA_MQTT_AVAILABLE, ReceiveMessage +from .models import DATA_MQTT, DATA_MQTT_AVAILABLE, MqttData, ReceiveMessage AVAILABILITY_TIMEOUT = 50.0 @@ -479,3 +482,43 @@ async def async_cleanup_device_registry( and not tag.async_has_tags(hass, device_id) ): device_registry.async_remove_device(device_id) + + +@callback +def async_remove_mqtt_issues(hass: HomeAssistant, mqtt_data: MqttData) -> None: + """Unregister open config issues.""" + issue_registry = ir.async_get(hass) + open_issues = [ + issue_id + for (domain, issue_id), issue_entry in issue_registry.issues.items() + if domain == DOMAIN and issue_entry.translation_key == "invalid_platform_config" + ] + for issue in open_issues: + ir.async_delete_issue(hass, DOMAIN, issue) + + +async def async_check_config_schema( + hass: HomeAssistant, config_yaml: ConfigType +) -> None: + """Validate manually configured MQTT items.""" + mqtt_data = hass.data[DATA_MQTT] + mqtt_config: list[dict[str, list[ConfigType]]] = config_yaml.get(DOMAIN, {}) + for mqtt_config_item in mqtt_config: + for domain, config_items in mqtt_config_item.items(): + schema = mqtt_data.reload_schema[domain] + for config in config_items: + try: + schema(config) + except probatio.Invalid as exc: + integration = await async_get_integration(hass, DOMAIN) + message = conf_util.format_schema_error( + hass, exc, domain, config, integration.documentation + ) + raise ServiceValidationError( + translation_domain=DOMAIN, + translation_key="invalid_platform_config_message", + translation_placeholders={ + "domain": domain, + "message": message, + }, + ) from exc