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 Paulus Schoutsen
parent 5068846fc9
commit d1bc6df14f
3 changed files with 63 additions and 35 deletions

View File

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

View File

@ -1,20 +1,31 @@
"""Support for AVM FRITZ!Box update platform.""" """Support for AVM FRITZ!Box update platform."""
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass
import logging import logging
from typing import Any 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.config_entries import ConfigEntry
from homeassistant.const import EntityCategory
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from .common import AvmWrapper, FritzBoxBaseEntity from .common import AvmWrapper, FritzBoxBaseCoordinatorEntity, FritzEntityDescription
from .const import DOMAIN from .const import DOMAIN
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
@dataclass
class FritzUpdateEntityDescription(UpdateEntityDescription, FritzEntityDescription):
"""Describes Fritz update entity."""
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None: ) -> None:
@ -27,11 +38,13 @@ async def async_setup_entry(
async_add_entities(entities) async_add_entities(entities)
class FritzBoxUpdateEntity(FritzBoxBaseEntity, UpdateEntity): class FritzBoxUpdateEntity(FritzBoxBaseCoordinatorEntity, UpdateEntity):
"""Mixin for update entity specific attributes.""" """Mixin for update entity specific attributes."""
_attr_entity_category = EntityCategory.CONFIG
_attr_supported_features = UpdateEntityFeature.INSTALL _attr_supported_features = UpdateEntityFeature.INSTALL
_attr_title = "FRITZ!OS" _attr_title = "FRITZ!OS"
entity_description: FritzUpdateEntityDescription
def __init__( def __init__(
self, self,
@ -39,29 +52,30 @@ class FritzBoxUpdateEntity(FritzBoxBaseEntity, UpdateEntity):
device_friendly_name: str, device_friendly_name: str,
) -> None: ) -> None:
"""Init FRITZ!Box connectivity class.""" """Init FRITZ!Box connectivity class."""
self._attr_name = f"{device_friendly_name} FRITZ!OS" description = FritzUpdateEntityDescription(
self._attr_unique_id = f"{avm_wrapper.unique_id}-update" key="update", name="FRITZ!OS", value_fn=None
super().__init__(avm_wrapper, device_friendly_name) )
super().__init__(avm_wrapper, device_friendly_name, description)
@property @property
def installed_version(self) -> str | None: def installed_version(self) -> str | None:
"""Version currently in use.""" """Version currently in use."""
return self._avm_wrapper.current_firmware return self.coordinator.current_firmware
@property @property
def latest_version(self) -> str | None: def latest_version(self) -> str | None:
"""Latest version available for install.""" """Latest version available for install."""
if self._avm_wrapper.update_available: if self.coordinator.update_available:
return self._avm_wrapper.latest_firmware return self.coordinator.latest_firmware
return self._avm_wrapper.current_firmware return self.coordinator.current_firmware
@property @property
def release_url(self) -> str | None: def release_url(self) -> str | None:
"""URL to the full release notes of the latest version available.""" """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( async def async_install(
self, version: str | None, backup: bool, **kwargs: Any self, version: str | None, backup: bool, **kwargs: Any
) -> None: ) -> None:
"""Install an update.""" """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.core import HomeAssistant
from homeassistant.setup import async_setup_component 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.common import MockConfigEntry
from tests.typing import ClientSessionGenerator 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( async def test_update_entities_initialized(
hass: HomeAssistant, hass: HomeAssistant,
@ -41,23 +55,21 @@ async def test_update_available(
) -> None: ) -> None:
"""Test update entities.""" """Test update entities."""
with patch( fc_class_mock().override_services({**MOCK_FB_SERVICES, **AVAILABLE_UPDATE})
"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)
assert await async_setup_component(hass, DOMAIN, {}) entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA)
await hass.async_block_till_done() entry.add_to_hass(hass)
assert entry.state == ConfigEntryState.LOADED
update = hass.states.get("update.mock_title_fritz_os") assert await async_setup_component(hass, DOMAIN, {})
assert update is not None await hass.async_block_till_done()
assert update.state == "on" assert entry.state == ConfigEntryState.LOADED
assert update.attributes.get("installed_version") == "7.29"
assert update.attributes.get("latest_version") == MOCK_FIRMWARE_AVAILABLE update = hass.states.get("update.mock_title_fritz_os")
assert update.attributes.get("release_url") == MOCK_FIRMWARE_RELEASE_URL 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( async def test_no_update_available(
@ -90,10 +102,9 @@ async def test_available_update_can_be_installed(
) -> None: ) -> None:
"""Test update entities.""" """Test update entities."""
fc_class_mock().override_services({**MOCK_FB_SERVICES, **AVAILABLE_UPDATE})
with patch( 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", "homeassistant.components.fritz.common.FritzBoxTools.async_trigger_firmware_update",
return_value=True, return_value=True,
) as mocked_update_call: ) as mocked_update_call: