From 618b16a32b64e277297a5784a7aab5662f0c0281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Wed, 30 Mar 2022 09:52:29 +0200 Subject: [PATCH] Add full release notes to add-on update entities (#68876) --- homeassistant/components/hassio/update.py | 42 ++++++++-- tests/components/hassio/test_update.py | 95 +++++++++++++++++++++++ 2 files changed, 129 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/hassio/update.py b/homeassistant/components/hassio/update.py index 19aee6dad37..41539aa21e0 100644 --- a/homeassistant/components/hassio/update.py +++ b/homeassistant/components/hassio/update.py @@ -88,39 +88,65 @@ async def async_setup_entry( class SupervisorAddonUpdateEntity(HassioAddonEntity, UpdateEntity): """Update entity to handle updates for the Supervisor add-ons.""" - _attr_supported_features = UpdateEntityFeature.INSTALL | UpdateEntityFeature.BACKUP + _attr_supported_features = ( + UpdateEntityFeature.INSTALL + | UpdateEntityFeature.BACKUP + | UpdateEntityFeature.RELEASE_NOTES + ) + + @property + def _addon_data(self) -> dict: + """Return the add-on data.""" + return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug] @property def title(self) -> str | None: """Return the title of the update.""" - return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_NAME] + return self._addon_data[ATTR_NAME] @property def latest_version(self) -> str | None: """Latest version available for install.""" - return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ - ATTR_VERSION_LATEST - ] + return self._addon_data[ATTR_VERSION_LATEST] @property def current_version(self) -> str | None: """Version currently in use.""" - return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_VERSION] + return self._addon_data[ATTR_VERSION] @property def release_summary(self) -> str | None: """Release summary for the add-on.""" - return self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_CHANGELOG] + return self._strip_release_notes() @property def entity_picture(self) -> str | None: """Return the icon of the add-on if any.""" if not self.available: return None - if self.coordinator.data[DATA_KEY_ADDONS][self._addon_slug][ATTR_ICON]: + if self._addon_data[ATTR_ICON]: return f"/api/hassio/addons/{self._addon_slug}/icon" return None + def _strip_release_notes(self) -> str | None: + """Strip the release notes to contain the needed sections.""" + if (notes := self._addon_data[ATTR_CHANGELOG]) is None: + return None + + if f"# {self.latest_version}" in notes and f"# {self.current_version}" in notes: + # Split the release notes to only what is between the versions if we can + new_notes = notes.split(f"# {self.current_version}")[0] + if f"# {self.latest_version}" in new_notes: + # Make sure the latest version is still there. + # This can be False if the order of the release notes are not correct + # In that case we just return the whole release notes + return new_notes + return notes + + async def async_release_notes(self) -> str | None: + """Return the release notes for the update.""" + return self._strip_release_notes() + async def async_install( self, version: str | None = None, diff --git a/tests/components/hassio/test_update.py b/tests/components/hassio/test_update.py index 3c5f7b52e56..8442f01abf3 100644 --- a/tests/components/hassio/test_update.py +++ b/tests/components/hassio/test_update.py @@ -370,3 +370,98 @@ async def test_update_core_with_error(hass, aioclient_mock): {"entity_id": "update.home_assistant_core_update"}, blocking=True, ) + + +async def test_release_notes_between_versions(hass, aioclient_mock, hass_ws_client): + """Test release notes between versions.""" + config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) + config_entry.add_to_hass(hass) + + with patch.dict(os.environ, MOCK_ENVIRON), patch( + "homeassistant.components.hassio.get_addons_changelogs", + return_value={"test": "# 2.0.1\nNew updates\n# 2.0.0\nOld updates"}, + ): + result = await async_setup_component( + hass, + "hassio", + {"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}}, + ) + assert result + await hass.async_block_till_done() + + client = await hass_ws_client(hass) + await hass.async_block_till_done() + + await client.send_json( + { + "id": 1, + "type": "update/release_notes", + "entity_id": "update.test_update", + } + ) + result = await client.receive_json() + assert "Old updates" not in result["result"] + assert "New updates" in result["result"] + + +async def test_release_notes_full(hass, aioclient_mock, hass_ws_client): + """Test release notes no match.""" + config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) + config_entry.add_to_hass(hass) + + with patch.dict(os.environ, MOCK_ENVIRON), patch( + "homeassistant.components.hassio.get_addons_changelogs", + return_value={"test": "# 2.0.0\nNew updates\n# 2.0.0\nOld updates"}, + ): + result = await async_setup_component( + hass, + "hassio", + {"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}}, + ) + assert result + await hass.async_block_till_done() + + client = await hass_ws_client(hass) + await hass.async_block_till_done() + + await client.send_json( + { + "id": 1, + "type": "update/release_notes", + "entity_id": "update.test_update", + } + ) + result = await client.receive_json() + assert "Old updates" in result["result"] + assert "New updates" in result["result"] + + +async def test_not_release_notes(hass, aioclient_mock, hass_ws_client): + """Test handling where there are no release notes.""" + config_entry = MockConfigEntry(domain=DOMAIN, data={}, unique_id=DOMAIN) + config_entry.add_to_hass(hass) + + with patch.dict(os.environ, MOCK_ENVIRON), patch( + "homeassistant.components.hassio.get_addons_changelogs", + return_value={"test": None}, + ): + result = await async_setup_component( + hass, + "hassio", + {"http": {"server_port": 9999, "server_host": "127.0.0.1"}, "hassio": {}}, + ) + assert result + await hass.async_block_till_done() + + client = await hass_ws_client(hass) + await hass.async_block_till_done() + + await client.send_json( + { + "id": 1, + "type": "update/release_notes", + "entity_id": "update.test_update", + } + ) + result = await client.receive_json() + assert result["result"] is None