diff --git a/homeassistant/components/fritz/common.py b/homeassistant/components/fritz/common.py index d7b8d916803..4a01367cc20 100644 --- a/homeassistant/components/fritz/common.py +++ b/homeassistant/components/fritz/common.py @@ -174,6 +174,7 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): self._current_firmware: str | None = None self._latest_firmware: str | None = None self._update_available: bool = False + self._release_url: str | None = None async def async_setup( self, options: MappingProxyType[str, Any] | None = None @@ -224,7 +225,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): self._model = info.get("NewModelName") self._current_firmware = info.get("NewSoftwareVersion") - self._update_available, self._latest_firmware = self._update_device_info() + ( + self._update_available, + self._latest_firmware, + self._release_url, + ) = self._update_device_info() if "Layer3Forwarding1" in self.connection.services: if connection_type := self.connection.call_action( "Layer3Forwarding1", "GetDefaultConnectionService" @@ -274,6 +279,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): """Return if new SW version is available.""" return self._update_available + @property + def release_url(self) -> str | None: + """Return the info URL for latest firmware.""" + return self._release_url + @property def mac(self) -> str: """Return device Mac address.""" @@ -305,12 +315,12 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): raise HomeAssistantError("Error refreshing hosts info") from ex return [] - def _update_device_info(self) -> tuple[bool, str | None]: + def _update_device_info(self) -> tuple[bool, str | None, str | None]: """Retrieve latest device information from the FRITZ!Box.""" - version = self.connection.call_action("UserInterface1", "GetInfo").get( - "NewX_AVM-DE_Version" - ) - return bool(version), version + info = self.connection.call_action("UserInterface1", "GetInfo") + version = info.get("NewX_AVM-DE_Version") + release_url = info.get("NewX_AVM-DE_InfoURL") + return bool(version), version, release_url def _get_wan_access(self, ip_address: str) -> bool | None: """Get WAN access rule for given IP address.""" @@ -361,7 +371,11 @@ class FritzBoxTools(update_coordinator.DataUpdateCoordinator): return _LOGGER.debug("Checking host info for FRITZ!Box device %s", self.host) - self._update_available, self._latest_firmware = self._update_device_info() + ( + self._update_available, + self._latest_firmware, + self._release_url, + ) = self._update_device_info() _LOGGER.debug("Checking devices for FRITZ!Box device %s", self.host) _default_consider_home = DEFAULT_CONSIDER_HOME.total_seconds() diff --git a/homeassistant/components/fritz/update.py b/homeassistant/components/fritz/update.py index 620b932999e..03cffc3cae6 100644 --- a/homeassistant/components/fritz/update.py +++ b/homeassistant/components/fritz/update.py @@ -55,6 +55,11 @@ class FritzBoxUpdateEntity(FritzBoxBaseEntity, UpdateEntity): return self._avm_wrapper.latest_firmware return self._avm_wrapper.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 + async def async_install( self, version: str | None, backup: bool, **kwargs: Any ) -> None: diff --git a/tests/components/fritz/const.py b/tests/components/fritz/const.py index 39533d07a93..f8f6f8370d7 100644 --- a/tests/components/fritz/const.py +++ b/tests/components/fritz/const.py @@ -30,8 +30,11 @@ MOCK_IPS = {"fritz.box": "192.168.178.1", "printer": "192.168.178.2"} MOCK_MODELNAME = "FRITZ!Box 7530 AX" MOCK_FIRMWARE = "256.07.29" MOCK_FIRMWARE_AVAILABLE = "256.07.50" +MOCK_FIRMWARE_RELEASE_URL = ( + "http://download.avm.de/fritzbox/fritzbox-7530-ax/deutschland/fritz.os/info_de.txt" +) MOCK_SERIAL_NUMBER = "fake_serial_number" -MOCK_FIRMWARE_INFO = [True, "1.1.1"] +MOCK_FIRMWARE_INFO = [True, "1.1.1", "some-release-url"] MOCK_MESH_SSID = "TestSSID" MOCK_MESH_MASTER_MAC = "1C:ED:6F:12:34:11" MOCK_MESH_MASTER_WIFI1_MAC = "1C:ED:6F:12:34:12" diff --git a/tests/components/fritz/test_update.py b/tests/components/fritz/test_update.py index f43b35755e8..2261ef3ef9b 100644 --- a/tests/components/fritz/test_update.py +++ b/tests/components/fritz/test_update.py @@ -10,7 +10,12 @@ from homeassistant.config_entries import ConfigEntryState from homeassistant.core import HomeAssistant from homeassistant.setup import async_setup_component -from .const import MOCK_FIRMWARE, MOCK_FIRMWARE_AVAILABLE, MOCK_USER_DATA +from .const import ( + MOCK_FIRMWARE, + MOCK_FIRMWARE_AVAILABLE, + MOCK_FIRMWARE_RELEASE_URL, + MOCK_USER_DATA, +) from tests.common import MockConfigEntry @@ -38,7 +43,7 @@ async def test_update_available( with patch( "homeassistant.components.fritz.common.FritzBoxTools._update_device_info", - return_value=(True, MOCK_FIRMWARE_AVAILABLE), + return_value=(True, MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL), ): entry = MockConfigEntry(domain=DOMAIN, data=MOCK_USER_DATA) entry.add_to_hass(hass) @@ -52,6 +57,7 @@ async def test_update_available( assert update.state == "on" assert update.attributes.get("installed_version") == MOCK_FIRMWARE 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( @@ -80,7 +86,7 @@ async def test_available_update_can_be_installed( with patch( "homeassistant.components.fritz.common.FritzBoxTools._update_device_info", - return_value=(True, MOCK_FIRMWARE_AVAILABLE), + return_value=(True, MOCK_FIRMWARE_AVAILABLE, MOCK_FIRMWARE_RELEASE_URL), ), patch( "homeassistant.components.fritz.common.FritzBoxTools.async_trigger_firmware_update", return_value=True,