Fix AVM Fritz!Tools update entity (#100151)

* move update entity to coordinator

* fix tests
This commit is contained in:
Michael 2023-09-12 04:30:50 +02:00 committed by GitHub
parent 15b9963a24
commit 3d28c6d636
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 63 additions and 35 deletions

View File

@ -1096,7 +1096,7 @@ class FritzBoxBaseEntity:
class FritzRequireKeysMixin:
"""Fritz entity description mix in."""
value_fn: Callable[[FritzStatus, Any], Any]
value_fn: Callable[[FritzStatus, Any], Any] | None
@dataclass
@ -1118,9 +1118,12 @@ class FritzBoxBaseCoordinatorEntity(update_coordinator.CoordinatorEntity[AvmWrap
) -> None:
"""Init device info class."""
super().__init__(avm_wrapper)
self.async_on_remove(
avm_wrapper.register_entity_updates(description.key, description.value_fn)
)
if description.value_fn is not None:
self.async_on_remove(
avm_wrapper.register_entity_updates(
description.key, description.value_fn
)
)
self.entity_description = description
self._device_name = device_name
self._attr_unique_id = f"{avm_wrapper.unique_id}-{description.key}"

View File

@ -1,20 +1,31 @@
"""Support for AVM FRITZ!Box update platform."""
from __future__ import annotations
from dataclasses import dataclass
import logging
from typing import Any
from homeassistant.components.update import UpdateEntity, UpdateEntityFeature
from homeassistant.components.update import (
UpdateEntity,
UpdateEntityDescription,
UpdateEntityFeature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .common import AvmWrapper, FritzBoxBaseEntity
from .common import AvmWrapper, FritzBoxBaseCoordinatorEntity, FritzEntityDescription
from .const import DOMAIN
_LOGGER = logging.getLogger(__name__)
@dataclass
class FritzUpdateEntityDescription(UpdateEntityDescription, FritzEntityDescription):
"""Describes Fritz update entity."""
async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
@ -27,11 +38,13 @@ async def async_setup_entry(
async_add_entities(entities)
class FritzBoxUpdateEntity(FritzBoxBaseEntity, UpdateEntity):
class FritzBoxUpdateEntity(FritzBoxBaseCoordinatorEntity, UpdateEntity):
"""Mixin for update entity specific attributes."""
_attr_entity_category = EntityCategory.CONFIG
_attr_supported_features = UpdateEntityFeature.INSTALL
_attr_title = "FRITZ!OS"
entity_description: FritzUpdateEntityDescription
def __init__(
self,
@ -39,29 +52,30 @@ class FritzBoxUpdateEntity(FritzBoxBaseEntity, UpdateEntity):
device_friendly_name: str,
) -> None:
"""Init FRITZ!Box connectivity class."""
self._attr_name = f"{device_friendly_name} FRITZ!OS"
self._attr_unique_id = f"{avm_wrapper.unique_id}-update"
super().__init__(avm_wrapper, device_friendly_name)
description = FritzUpdateEntityDescription(
key="update", name="FRITZ!OS", value_fn=None
)
super().__init__(avm_wrapper, device_friendly_name, description)
@property
def installed_version(self) -> str | None:
"""Version currently in use."""
return self._avm_wrapper.current_firmware
return self.coordinator.current_firmware
@property
def latest_version(self) -> str | None:
"""Latest version available for install."""
if self._avm_wrapper.update_available:
return self._avm_wrapper.latest_firmware
return self._avm_wrapper.current_firmware
if self.coordinator.update_available:
return self.coordinator.latest_firmware
return self.coordinator.current_firmware
@property
def release_url(self) -> str | None:
"""URL to the full release notes of the latest version available."""
return self._avm_wrapper.release_url
return self.coordinator.release_url
async def async_install(
self, version: str | None, backup: bool, **kwargs: Any
) -> None:
"""Install an update."""
await self._avm_wrapper.async_trigger_firmware_update()
await self.coordinator.async_trigger_firmware_update()

View File

@ -8,11 +8,25 @@ from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from .const import MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL, MOCK_USER_DATA
from .const import (
MOCK_FB_SERVICES,
MOCK_FIRMWARE_AVAILABLE,
MOCK_FIRMWARE_RELEASE_URL,
MOCK_USER_DATA,
)
from tests.common import MockConfigEntry
from tests.typing import ClientSessionGenerator
AVAILABLE_UPDATE = {
"UserInterface1": {
"GetInfo": {
"NewX_AVM-DE_Version": MOCK_FIRMWARE_AVAILABLE,
"NewX_AVM-DE_InfoURL": MOCK_FIRMWARE_RELEASE_URL,
},
}
}
async def test_update_entities_initialized(
hass: HomeAssistant,
@ -41,23 +55,21 @@ async def test_update_available(
) -> None:
"""Test update entities."""
with patch(
"homeassistant.components.fritz.common.FritzBoxTools._update_device_info",
return_value=(True, MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL),
):
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
fc_class_mock().override_services({**MOCK_FB_SERVICES, **AVAILABLE_UPDATE})
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
entry.add_to_hass(hass)
update = hass.states.get("update.mock_title_fritz_os")
assert update is not None
assert update.state == "on"
assert update.attributes.get("installed_version") == "7.29"
assert update.attributes.get("latest_version") == MOCK_FIRMWARE_AVAILABLE
assert update.attributes.get("release_url") == MOCK_FIRMWARE_RELEASE_URL
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert entry.state == ConfigEntryState.LOADED
update = hass.states.get("update.mock_title_fritz_os")
assert update is not None
assert update.state == "on"
assert update.attributes.get("installed_version") == "7.29"
assert update.attributes.get("latest_version") == MOCK_FIRMWARE_AVAILABLE
assert update.attributes.get("release_url") == MOCK_FIRMWARE_RELEASE_URL
async def test_no_update_available(
@ -90,10 +102,9 @@ async def test_available_update_can_be_installed(
) -> None:
"""Test update entities."""
fc_class_mock().override_services({**MOCK_FB_SERVICES, **AVAILABLE_UPDATE})
with patch(
"homeassistant.components.fritz.common.FritzBoxTools._update_device_info",
return_value=(True, MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL),
), patch(
"homeassistant.components.fritz.common.FritzBoxTools.async_trigger_firmware_update",
return_value=True,
) as mocked_update_call: