Compare commits

...
Sign in to create a new pull request.

3 commits

Author SHA1 Message Date
Stefan Agner
9726228630 Address mypy issues 2024-06-05 10:35:32 +02:00
Stefan Agner
b278976bc2 Add tests 2024-06-05 10:35:32 +02:00
Stefan Agner
0754fdcf12 Automatically update matter server add-on if needed
Update to the latest add-on if the schema version of the client is
newer than the Server schema version.
2024-06-05 10:35:32 +02:00
2 changed files with 71 additions and 24 deletions

View file

@ -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,10 +73,8 @@ 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:
# 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,
@ -84,13 +84,33 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
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:

View file

@ -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",