diff --git a/homeassistant/components/matter/__init__.py b/homeassistant/components/matter/__init__.py index 86b642f7389..ad77d8cc2f1 100644 --- a/homeassistant/components/matter/__init__.py +++ b/homeassistant/components/matter/__init__.py @@ -8,9 +8,11 @@ from functools import cache from matter_server.client import MatterClient from matter_server.client.exceptions import CannotConnect, InvalidServerVersion +from matter_server.common.const import SCHEMA_VERSION from matter_server.common.errors import MatterError, NodeNotExists from homeassistant.components.hassio import AddonError, AddonManager, AddonState +from homeassistant.components.hassio.addon_manager import AddonInfo from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import CONF_URL, EVENT_HOMEASSISTANT_STOP from homeassistant.core import Event, HomeAssistant, callback @@ -62,7 +64,7 @@ def get_matter_device_info( async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: """Set up Matter from a config entry.""" if use_addon := entry.data.get(CONF_USE_ADDON): - await _async_ensure_addon_running(hass, entry) + addon_info = await _async_ensure_addon_running(hass, entry) matter_client = MatterClient(entry.data[CONF_URL], async_get_clientsession(hass)) try: @@ -71,26 +73,44 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except (CannotConnect, TimeoutError) as err: raise ConfigEntryNotReady("Failed to connect to matter server") from err except InvalidServerVersion as err: - if use_addon: - addon_manager = _get_addon_manager(hass) - addon_manager.async_schedule_update_addon(catch_error=True) - else: - async_create_issue( - hass, - DOMAIN, - "invalid_server_version", - is_fixable=False, - severity=IssueSeverity.ERROR, - translation_key="invalid_server_version", - ) + # This is raised when the Server requires a newer client than we are :( + # We can't do anything about it even in the add-on case. + async_create_issue( + hass, + DOMAIN, + "invalid_server_version", + is_fixable=False, + severity=IssueSeverity.ERROR, + translation_key="invalid_server_version", + ) raise ConfigEntryNotReady(f"Invalid server version: {err}") from err - except Exception as err: LOGGER.exception("Failed to connect to matter server") raise ConfigEntryNotReady( "Unknown error connecting to the Matter server" ) from err + if ( + matter_client.server_info is not None + and matter_client.server_info.schema_version < SCHEMA_VERSION + ): + if use_addon and addon_info.update_available: + LOGGER.info( + "Matter server schema version is too old (%d < %d), scheduling add-on update", + matter_client.server_info.schema_version, + SCHEMA_VERSION, + ) + await matter_client.disconnect() + addon_manager = _get_addon_manager(hass) + addon_manager.async_schedule_update_addon(catch_error=True) + raise ConfigEntryNotReady("Matter server version too old") + + LOGGER.warning( + "Matter server schema version is too old (%d < %d)", + matter_client.server_info.schema_version, + SCHEMA_VERSION, + ) + async_delete_issue(hass, DOMAIN, "invalid_server_version") async def on_hass_stop(event: Event) -> None: @@ -238,8 +258,13 @@ async def async_remove_config_entry_device( return True -async def _async_ensure_addon_running(hass: HomeAssistant, entry: ConfigEntry) -> None: - """Ensure that Matter Server add-on is installed and running.""" +async def _async_ensure_addon_running( + hass: HomeAssistant, entry: ConfigEntry +) -> AddonInfo: + """Ensure that Matter Server add-on is installed and running. + + Returns add-on information if successful, raises ConfigEntryNotReady otherwise. + """ addon_manager = _get_addon_manager(hass) try: addon_info = await addon_manager.async_get_addon_info() @@ -259,6 +284,8 @@ async def _async_ensure_addon_running(hass: HomeAssistant, entry: ConfigEntry) - addon_manager.async_schedule_start_addon(catch_error=True) raise ConfigEntryNotReady + return addon_info + @callback def _get_addon_manager(hass: HomeAssistant) -> AddonManager: diff --git a/tests/components/matter/test_init.py b/tests/components/matter/test_init.py index 6e0a22188ec..2a061fd8d51 100644 --- a/tests/components/matter/test_init.py +++ b/tests/components/matter/test_init.py @@ -8,6 +8,7 @@ from unittest.mock import AsyncMock, MagicMock, call, patch from matter_server.client.exceptions import CannotConnect, InvalidServerVersion from matter_server.client.models.node import MatterNode +from matter_server.common.const import SCHEMA_VERSION from matter_server.common.errors import MatterError from matter_server.common.helpers.util import dataclass_from_dict from matter_server.common.models import MatterNodeData @@ -356,18 +357,36 @@ async def test_addon_info_failure( @pytest.mark.parametrize( ( - "addon_version", "update_available", "update_calls", "backup_calls", "update_addon_side_effect", "create_backup_side_effect", + "matter_client_connect_side_effect", + "matter_client_server_info_schema_version", ), [ - ("1.0.0", True, 1, 1, None, None), - ("1.0.0", False, 0, 0, None, None), - ("1.0.0", True, 1, 1, HassioAPIError("Boom"), None), - ("1.0.0", True, 0, 1, None, HassioAPIError("Boom")), + (True, 1, 1, None, None, None, 1), + ( + False, + 0, + 0, + None, + None, + InvalidServerVersion("Invalid version"), + 1, + ), + ( + False, + 0, + 0, + None, + None, + InvalidServerVersion("Invalid version"), + SCHEMA_VERSION, + ), + (True, 1, 1, HassioAPIError("Boom"), None, None, 1), + (True, 0, 1, None, HassioAPIError("Boom"), None, 1), ], ) async def test_update_addon( @@ -380,19 +399,20 @@ async def test_update_addon( create_backup: AsyncMock, update_addon: AsyncMock, matter_client: MagicMock, - addon_version: str, update_available: bool, update_calls: int, backup_calls: int, update_addon_side_effect: Exception | None, create_backup_side_effect: Exception | None, + matter_client_connect_side_effect: Exception | None, + matter_client_server_info_schema_version: int, ): """Test update the Matter add-on during entry setup.""" - addon_info.return_value["version"] = addon_version addon_info.return_value["update_available"] = update_available create_backup.side_effect = create_backup_side_effect update_addon.side_effect = update_addon_side_effect - matter_client.connect.side_effect = InvalidServerVersion("Invalid version") + matter_client.connect.side_effect = matter_client_connect_side_effect + matter_client.server_info.schema_version = matter_client_server_info_schema_version entry = MockConfigEntry( domain=DOMAIN, title="Matter",