-
-
Notifications
You must be signed in to change notification settings - Fork 37.8k
Add update integration #66552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Add update integration #66552
Changes from 9 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
e1cb86b
Repurpose the Updater integration
ludeeus 9b395da
Remove debug
ludeeus 014b9b8
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus 58c2e97
Pass updater_data to _get_update_details
ludeeus cda5149
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus bf1bce0
split update to own module, and leave updater as is
ludeeus e76b217
Add changelog_content
ludeeus cad343b
adjustments after split
ludeeus 7f5e525
Update script/scaffold/templates/update/integration/update.py
ludeeus 7324fe2
Remove scaffold
ludeeus 52554b9
update
ludeeus 4b4cb32
Add delay
ludeeus 3b4c4c7
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus 36a6d0f
fix type conflict
ludeeus ba2c188
Add missing coverage
ludeeus 05260ed
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus 0a7a43d
Load when needed
ludeeus 3c7b1b8
updates
ludeeus 3592149
rename WS function
ludeeus e846eb6
remove
ludeeus d95c552
Update homeassistant/components/update/__init__.py
ludeeus 0c914f5
Update homeassistant/components/update/__init__.py
ludeeus eefa61a
Move platform registration
ludeeus 6f0821a
Changes needed after suggestion
ludeeus 0f154ee
Only register /info during setup
ludeeus 5f4a41f
Better error
ludeeus 36b86f3
Use supported_features
ludeeus d6023cf
Move assert
ludeeus b184f3a
Use dataclasses.field
ludeeus 95741d2
Fix test
ludeeus c580b23
Adjust working
ludeeus e0a9e87
Remove assert
ludeeus 394ca7b
Load if not loaded in domain_is_valid
ludeeus d74e862
Remove _register_update_platform
ludeeus 69fa06e
Use supports_backup
ludeeus a4a8f1a
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus 29a4898
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus 0977a15
print the stack trace if it's an unexpected exception
ludeeus 51e87bf
Merge branch 'dev' of github.com:home-assistant/core into repurpose_u…
ludeeus a1a3475
Add UpdateFailed exception
ludeeus a4801eb
coverage
ludeeus 903d2ce
Apply suggestions from code review
ludeeus fda8db3
adjust
ludeeus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,243 @@ | ||
| """Support for Update.""" | ||
| from __future__ import annotations | ||
|
|
||
| import asyncio | ||
| from collections.abc import Awaitable, Callable | ||
| import dataclasses | ||
| import logging | ||
|
|
||
| import async_timeout | ||
| import voluptuous as vol | ||
|
|
||
| from homeassistant.components import websocket_api | ||
| from homeassistant.core import HomeAssistant, callback | ||
| from homeassistant.helpers import integration_platform, storage | ||
| from homeassistant.helpers.typing import ConfigType | ||
|
|
||
| _LOGGER = logging.getLogger(__name__) | ||
|
|
||
| DOMAIN = "update" | ||
|
|
||
| INFO_CALLBACK_TIMEOUT = 5 | ||
| STORAGE_VERSION = 1 | ||
|
|
||
|
|
||
| async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool: | ||
| """Set up the Update integration.""" | ||
| store = storage.Store( | ||
| hass=hass, | ||
| version=STORAGE_VERSION, | ||
| key=DOMAIN, | ||
| ) | ||
| hass.data[DOMAIN] = UpdateData( | ||
| store=store, | ||
| skip=set(await store.async_load() or []), | ||
| ) | ||
|
|
||
| websocket_api.async_register_command(hass, handle_info) | ||
| websocket_api.async_register_command(hass, handle_update) | ||
| websocket_api.async_register_command(hass, handle_skip) | ||
|
|
||
| await integration_platform.async_process_integration_platforms( | ||
| hass, DOMAIN, _register_update_platform | ||
| ) | ||
|
|
||
| return True | ||
|
|
||
|
|
||
| async def _register_update_platform(hass, integration_domain, platform) -> None: | ||
| """Register a update platform.""" | ||
|
ludeeus marked this conversation as resolved.
Outdated
|
||
| if hasattr(platform, "async_register"): | ||
| platform.async_register(UpdateRegistration(hass, integration_domain)) | ||
|
ludeeus marked this conversation as resolved.
Outdated
|
||
|
|
||
|
|
||
| async def get_integration_info( | ||
| hass: HomeAssistant, | ||
| registration: UpdateRegistration, | ||
| ) -> list[UpdateDescription] | None: | ||
| """Get integration update details.""" | ||
| assert registration.updates_callback | ||
|
|
||
| try: | ||
| async with async_timeout.timeout(INFO_CALLBACK_TIMEOUT): | ||
| return await registration.updates_callback(hass) | ||
| except asyncio.TimeoutError: | ||
| _LOGGER.warning("Timeout while getting updates from %s", registration.domain) | ||
| except Exception: # pylint: disable=broad-except | ||
| _LOGGER.exception("Error fetching info") | ||
| return None | ||
|
|
||
|
|
||
| def _get_update_details( | ||
| update_data: UpdateData, | ||
| domain: str, | ||
| identifier: str, | ||
| ) -> UpdateDescription | None: | ||
| """Get an update.""" | ||
| return next( | ||
| ( | ||
| update | ||
| for update in update_data.updates.get(domain, []) | ||
| if update.identifier == identifier | ||
| ), | ||
| None, | ||
| ) | ||
|
|
||
|
|
||
| def _filtered_updates(update_data: UpdateData) -> list[dict]: | ||
| """Return a list of updates that are not skipped.""" | ||
| return [ | ||
| { | ||
| "domain": domain, | ||
| "identifier": description.identifier, | ||
| "name": description.name, | ||
| "current_version": description.current_version, | ||
| "available_version": description.available_version, | ||
| "changelog_url": description.changelog_url, | ||
| "icon_url": description.icon_url, | ||
| } | ||
| for domain, domain_data in update_data.updates.items() | ||
| if domain_data is not None | ||
| for description in domain_data | ||
| if f"{domain}_{description.identifier}_{description.available_version}" | ||
| not in update_data.skip | ||
| ] | ||
|
|
||
|
|
||
| @websocket_api.websocket_command({vol.Required("type"): "update/info"}) | ||
| @websocket_api.async_response | ||
| async def handle_info( | ||
| hass: HomeAssistant, | ||
| connection: websocket_api.ActiveConnection, | ||
| msg: dict, | ||
| ): | ||
| """Get pending updates from all platforms.""" | ||
| update_data: UpdateData = hass.data[DOMAIN] | ||
|
|
||
| for domain, domain_data in zip( | ||
| update_data.registrations, | ||
| await asyncio.gather( | ||
| *( | ||
| get_integration_info(hass, registration) | ||
| for registration in update_data.registrations.values() | ||
| ) | ||
| ), | ||
| ): | ||
| update_data.updates[domain] = domain_data | ||
|
|
||
| connection.send_result(msg["id"], _filtered_updates(update_data)) | ||
|
|
||
|
|
||
| @websocket_api.websocket_command( | ||
| { | ||
| vol.Required("type"): "update/skip", | ||
| vol.Required("domain"): str, | ||
| vol.Required("identifier"): str, | ||
| } | ||
| ) | ||
| @websocket_api.async_response | ||
| async def handle_skip( | ||
| hass: HomeAssistant, | ||
| connection: websocket_api.ActiveConnection, | ||
| msg: dict, | ||
| ): | ||
| """Skip an update.""" | ||
| update_data: UpdateData = hass.data[DOMAIN] | ||
| update_details = _get_update_details(update_data, msg["domain"], msg["identifier"]) | ||
| if update_details is not None: | ||
| update_data.skip.add( | ||
| f"{msg['domain']}_{update_details.identifier}_{update_details.available_version}" | ||
| ) | ||
| update_data.updates[msg["domain"]].remove(update_details) | ||
| await update_data.store.async_save(list(update_data.skip)) | ||
|
ludeeus marked this conversation as resolved.
Outdated
|
||
|
|
||
| connection.send_result( | ||
| msg["id"], | ||
| _filtered_updates(update_data), | ||
| ) | ||
|
|
||
|
|
||
| @websocket_api.websocket_command( | ||
| { | ||
| vol.Required("type"): "update/update", | ||
|
ludeeus marked this conversation as resolved.
|
||
| vol.Required("domain"): str, | ||
| vol.Required("identifier"): str, | ||
| } | ||
| ) | ||
| @websocket_api.async_response | ||
| async def handle_update( | ||
| hass: HomeAssistant, | ||
| connection: websocket_api.ActiveConnection, | ||
| msg: dict, | ||
| ): | ||
| """Handle an update.""" | ||
| update_data: UpdateData = hass.data[DOMAIN] | ||
| update_details = _get_update_details(update_data, msg["domain"], msg["identifier"]) | ||
|
|
||
| if update_details is None: | ||
| connection.send_error( | ||
| msg["id"], | ||
| "not_found", | ||
| f"No updates found for {msg['domain']} and {msg['identifier']}", | ||
| ) | ||
| return | ||
|
|
||
| if not await update_details.update_callback(hass, update_details): | ||
| connection.send_error(msg["id"], "update_failed", "Update failed") | ||
|
ludeeus marked this conversation as resolved.
Outdated
|
||
| return | ||
|
|
||
| update_data.updates[msg["domain"]].remove(update_details) | ||
|
|
||
| connection.send_result( | ||
| msg["id"], | ||
| _filtered_updates(update_data), | ||
| ) | ||
|
|
||
|
|
||
| @dataclasses.dataclass() | ||
| class UpdateData: | ||
| """Data for the update integration.""" | ||
|
|
||
| store: storage.Store | ||
| skip: set[str] | ||
| registrations: dict[str, UpdateRegistration] = dataclasses.field( | ||
| default_factory=dict | ||
| ) | ||
| updates: dict[str, list[UpdateDescription]] = dataclasses.field( | ||
| default_factory=dict | ||
| ) | ||
|
|
||
|
|
||
| @dataclasses.dataclass() | ||
| class UpdateDescription: | ||
| """Describe an update update.""" | ||
|
|
||
| identifier: str | ||
| name: str | ||
| current_version: str | ||
| available_version: str | ||
| update_callback: Callable[[HomeAssistant, UpdateDescription], Awaitable[bool]] | ||
| changelog_content: str | None = None | ||
| changelog_url: str | None = None | ||
| icon_url: str | None = None | ||
|
|
||
|
|
||
| @dataclasses.dataclass() | ||
| class UpdateRegistration: | ||
| """Helper class to track platform registration.""" | ||
|
|
||
| hass: HomeAssistant | ||
| domain: str | ||
| updates_callback: Callable[ | ||
| [HomeAssistant], Awaitable[list[UpdateDescription]] | ||
| ] | None = None | ||
|
|
||
| @callback | ||
| def async_register( | ||
| self, | ||
| updates_callback: Callable[[HomeAssistant], Awaitable[list[UpdateDescription]]], | ||
| ): | ||
| """Register the updates info callback.""" | ||
| update_data: UpdateData = self.hass.data[DOMAIN] | ||
| self.updates_callback = updates_callback | ||
| update_data.registrations[self.domain] = self | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| { | ||
| "domain": "update", | ||
| "name": "Update", | ||
| "documentation": "https://www.home-assistant.io/integrations/update", | ||
| "codeowners": [ | ||
| "@home-assistant/core" | ||
| ], | ||
| "quality_scale": "internal", | ||
| "iot_class": "calculated" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "title": "Update" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "title": "Update" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| """Update support for the NEW_NAME integration.""" | ||
|
|
||
| from homeassistant.components.update import UpdateDescription, UpdateRegistration | ||
| from homeassistant.core import HomeAssistant, callback | ||
|
|
||
|
|
||
| @callback | ||
| def async_register(registration: UpdateRegistration) -> None: | ||
| """Register the update handler.""" | ||
| registration.async_register(get_pending_updates) | ||
|
|
||
|
|
||
| async def get_pending_updates(hass: HomeAssistant) -> list[UpdateDescription]: | ||
| """Get pending updates.""" | ||
| # TODO: Add your logic to gather updates here. | ||
| # And return a list of UpdateDescription objects. | ||
| return [] | ||
|
|
||
|
|
||
| async def handle_update( | ||
| hass: HomeAssistant, | ||
| update_details: UpdateDescription, | ||
| ) -> bool: | ||
| """Handle an update.""" | ||
| # TODO: Add your logic to perform updates here. | ||
| # And return a bool to indicate if the update where successful or not. | ||
| return True |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| """Test the NEW_NAME update platform.""" | ||
| from homeassistant.components.NEW_DOMAIN.const import DOMAIN | ||
| from homeassistant.core import HomeAssistant | ||
|
|
||
| from tests.common import get_integration_updates | ||
|
|
||
|
|
||
| async def test_pending_updates(hass: HomeAssistant) -> None: | ||
| """Test getting NEW_NAME updates.""" | ||
| assert await get_integration_updates(hass, DOMAIN) == [] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| """Tests for the Update integration.""" |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.