"""Support for update entities.""" from dataclasses import dataclass from typing import Any, cast, override from homeassistant.components.update import ( UpdateDeviceClass, UpdateEntity, UpdateEntityDescription, UpdateEntityFeature, ) from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import ( ATTR_ROUTERBOARD_FIRMWARE, ATTR_SYSTEM_FIRMWARE, BACKUP, MIKROTIK_SERVICES, ROUTERBOARD, UPDATE, ) from .coordinator import MikrotikConfigEntry, mikrotik_config_entry_errors from .entity import MikrotikEntity PARALLEL_UPDATES = 0 @dataclass(frozen=True, kw_only=True) class MikrotikUpdateEntityDescription(UpdateEntityDescription): """Shared Mikrotik Update entity description.""" release_notes: str | None = None supported_features: UpdateEntityFeature path: str installed_version: str latest_version: str UPDATES = { MikrotikUpdateEntityDescription( key="fw-update", translation_key="firmware_update", device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, supported_features=UpdateEntityFeature.INSTALL | UpdateEntityFeature.BACKUP, path=UPDATE, installed_version=ATTR_SYSTEM_FIRMWARE, latest_version="latest-version", release_notes="https://cdn.mikrotik.com/routeros/{version}/CHANGELOG", ), MikrotikUpdateEntityDescription( key="routerboard-update", translation_key="routerboard_update", device_class=UpdateDeviceClass.FIRMWARE, entity_category=EntityCategory.CONFIG, supported_features=UpdateEntityFeature.INSTALL, path=ROUTERBOARD, installed_version=ATTR_ROUTERBOARD_FIRMWARE, latest_version="upgrade-firmware", ), } async def async_setup_entry( hass: HomeAssistant, entry: MikrotikConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up Mikrotik update entities.""" coordinator = entry.runtime_data update_list = [ MikrotikUpdateEntity(coordinator, update_desc) for update_desc in UPDATES if update_desc.path in coordinator.api.system ] async_add_entities(update_list) class MikrotikUpdateEntity(MikrotikEntity, UpdateEntity): """Mixin for update entity specific attributes.""" entity_description: MikrotikUpdateEntityDescription @property @override def supported_features(self) -> UpdateEntityFeature: """Flag supported features.""" return self.entity_description.supported_features @property def _device_path_info(self) -> dict[str, Any]: return cast( dict[str, Any], self.coordinator.api.system[self.entity_description.path] ) @property @override def installed_version(self) -> str | None: """Version currently in use.""" return self._device_path_info.get(self.entity_description.installed_version) @property @override def latest_version(self) -> str | None: """Latest version available for install.""" if latest_version := self._device_path_info.get( self.entity_description.latest_version ): return cast(str | None, latest_version) return self._device_path_info.get(self.entity_description.installed_version) @property @override def release_url(self) -> str | None: """URL to the full release notes of the latest version available.""" if self.entity_description.release_notes: return str(self.entity_description.release_notes).format( version=self.latest_version ) return None @override async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: """Install an update.""" with mikrotik_config_entry_errors(): if backup: await self.hass.async_add_executor_job( self.coordinator.api.command, MIKROTIK_SERVICES[BACKUP], ) await self.hass.async_add_executor_job( self.coordinator.api.command, MIKROTIK_SERVICES[self.entity_description.key], )