diff --git a/custom_components/mikrotik_router/update.py b/custom_components/mikrotik_router/update.py index 83c032e..ef01cec 100644 --- a/custom_components/mikrotik_router/update.py +++ b/custom_components/mikrotik_router/update.py @@ -2,6 +2,7 @@ from __future__ import annotations +import asyncio from logging import getLogger from typing import Any @@ -22,6 +23,7 @@ from .update_types import ( SENSOR_TYPES, SENSOR_SERVICES, ) +from packaging.version import Version _LOGGER = getLogger(__name__) DEVICE_UPDATE = "device_update" @@ -92,17 +94,18 @@ class MikrotikRouterOSUpdate(MikrotikEntity, UpdateEntity): """Return the release notes.""" try: session = async_get_clientsession(self.hass) - async with session.get( - f"https://cdn.mikrotik.com/routeros/{self._data['latest-version']}/CHANGELOG" - ) as response: - if response.status == 200: - text = await response.text() - return text.replace("*) ", "- ") - else: - _LOGGER.warning( - "Failed to fetch release notes due to a network error." - ) - return "Failed to fetch release notes due to a network error." + """Get concatenated changelogs from installed_version to latest_version in reverse order.""" + versions_to_fetch = generate_version_list( + self._data["installed-version"], self._data["latest-version"] + ) + + tasks = [fetch_changelog(session, version) for version in versions_to_fetch] + changelogs = await asyncio.gather(*tasks) + + # Combine all non-empty changelogs, maintaining reverse order + combined_changelogs = "\n".join(filter(None, changelogs)) + return combined_changelogs.replace("*) ", "- ") + except Exception as e: _LOGGER.warning("Failed to download release notes (%s)", e) @@ -160,3 +163,47 @@ class MikrotikRouterBoardFWUpdate(MikrotikEntity, UpdateEntity): """Install an update.""" self.coordinator.execute("/system/routerboard", "upgrade", None, None) self.coordinator.execute("/system", "reboot", None, None) + + +async def fetch_changelog(session, version: str) -> str: + """Asynchronously fetch the changelog for a given version.""" + url = f"https://cdn.mikrotik.com/routeros/{version}/CHANGELOG" + try: + async with session.get(url) as response: + if response.status == 200: + text = await response.text() + return text.replace("*) ", "- ") + except Exception as e: + pass + return "" + + +def generate_version_list(start_version: str, end_version: str) -> list: + """Generate a list of version strings from start_version to end_version in reverse order.""" + start = Version(start_version) + end = Version(end_version) + versions = [] + + current = end + while current >= start: + versions.append(str(current)) + current = decrement_version(current, start) + + return versions + + +def decrement_version(version: Version, start_version: Version) -> Version: + """Decrement version by the smallest possible step without going below start_version.""" + if version.micro > 0: + next_patch = version.micro - 1 + return Version(f"{version.major}.{version.minor}.{next_patch}") + elif version.minor > 0: + next_minor = version.minor - 1 + return Version( + f"{version.major}.{next_minor}.999" + ) # Assuming .999 as max patch version + else: + next_major = version.major - 1 + return Version( + f"{next_major}.999.999" + ) # Assuming .999 as max minor and patch version