tomaae.homeassistant-mikrot.../custom_components/mikrotik_router/update.py
Sergey Krashevich ccc097c438
feat(mikrotik_router): switch to aiohttp for async release notes fetching
This commit replaces the synchronous requests library with aiohttp for asynchronous fetching of Mikrotik RouterOS update release notes. It introduces the use of Home Assistant's built-in `async_create_clientsession` to manage HTTP sessions, enhancing the integration's performance and reliability by leveraging asyncio for network operations.

The change aims to improve the responsiveness and efficiency of the Mikrotik Router integration within Home Assistant, particularly in retrieving release notes for updates. By moving to an asynchronous model, the integration can now fetch data without blocking the execution of other tasks, leading to a smoother user experience.

Additionally, the error handling has been updated to provide clearer messages in the log when the release notes cannot be fetched, either due to a network error or an exception. This improvement aids in troubleshooting and ensures that users are better informed about the status of their update checks.
2024-04-16 17:57:02 +03:00

163 lines
5.3 KiB
Python

"""Support for the Mikrotik Router update service."""
from __future__ import annotations
from logging import getLogger
import aiohttp
from typing import Any
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.components.update import (
UpdateEntity,
UpdateDeviceClass,
UpdateEntityFeature,
)
from .coordinator import MikrotikCoordinator
from .entity import MikrotikEntity, async_add_entities
from .update_types import (
SENSOR_TYPES,
SENSOR_SERVICES,
)
_LOGGER = getLogger(__name__)
DEVICE_UPDATE = "device_update"
# ---------------------------
# async_setup_entry
# ---------------------------
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
_async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up entry for component"""
dispatcher = {
"MikrotikRouterOSUpdate": MikrotikRouterOSUpdate,
"MikrotikRouterBoardFWUpdate": MikrotikRouterBoardFWUpdate,
}
await async_add_entities(hass, config_entry, dispatcher)
# ---------------------------
# MikrotikRouterOSUpdate
# ---------------------------
class MikrotikRouterOSUpdate(MikrotikEntity, UpdateEntity):
"""Define an Mikrotik Controller Update entity."""
def __init__(
self,
coordinator: MikrotikCoordinator,
entity_description,
uid: str | None = None,
):
"""Set up device update entity."""
super().__init__(coordinator, entity_description, uid)
self._attr_supported_features = UpdateEntityFeature.INSTALL
self._attr_supported_features |= UpdateEntityFeature.BACKUP
self._attr_supported_features |= UpdateEntityFeature.RELEASE_NOTES
self._attr_title = self.entity_description.title
@property
def is_on(self) -> bool:
"""Return true if device is on."""
return self._data[self.entity_description.data_attribute]
@property
def installed_version(self) -> str:
"""Version installed and in use."""
return self._data["installed-version"]
@property
def latest_version(self) -> str:
"""Latest version available for install."""
return self._data["latest-version"]
async def options_updated(self) -> None:
"""No action needed."""
async def async_install(self, version: str, backup: bool, **kwargs: Any) -> None:
"""Install an update."""
if backup:
self.coordinator.execute("/system/backup", "save", None, None)
self.coordinator.execute("/system/package/update", "install", None, None)
async def async_release_notes(self) -> str:
"""Return the release notes."""
try:
async with async_create_clientsession(self.hass) as session:
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."
except Exception as e:
_LOGGER.warning("Failed to download release notes (%s)", e)
return "Error fetching release notes."
@property
def release_url(self) -> str:
"""URL to the full release notes of the latest version available."""
return "https://mikrotik.com/download/changelogs"
# ---------------------------
# MikrotikRouterBoardFWUpdate
# ---------------------------
class MikrotikRouterBoardFWUpdate(MikrotikEntity, UpdateEntity):
"""Define an Mikrotik Controller Update entity."""
TYPE = DEVICE_UPDATE
_attr_device_class = UpdateDeviceClass.FIRMWARE
def __init__(
self,
coordinator: MikrotikCoordinator,
entity_description,
uid: str | None = None,
):
"""Set up device update entity."""
super().__init__(coordinator, entity_description, uid)
self._attr_supported_features = UpdateEntityFeature.INSTALL
self._attr_title = self.entity_description.title
@property
def is_on(self) -> bool:
"""Return true if device is on."""
return (
self.data["routerboard"]["current-firmware"]
!= self.data["routerboard"]["upgrade-firmware"]
)
@property
def installed_version(self) -> str:
"""Version installed and in use."""
return self._data["current-firmware"]
@property
def latest_version(self) -> str:
"""Latest version available for install."""
return self._data["upgrade-firmware"]
async def options_updated(self) -> None:
"""No action needed."""
async def async_install(self, version: str, backup: bool, **kwargs: Any) -> None:
"""Install an update."""
self.coordinator.execute("/system/routerboard", "upgrade", None, None)
self.coordinator.execute("/system", "reboot", None, None)