mirror of
https://github.com/home-assistant/developers.home-assistant.git
synced 2025-07-13 12:26:29 +00:00
Add new backup platform interface (#2514)
Co-authored-by: Abílio Costa <abmantis@users.noreply.github.com>
This commit is contained in:
parent
c5c3d94f29
commit
4d2ede8eed
@ -2,11 +2,133 @@
|
|||||||
title: "Backup"
|
title: "Backup"
|
||||||
---
|
---
|
||||||
|
|
||||||
|
There are two main purposes for an integration to implement a backup platform:
|
||||||
|
|
||||||
|
1. Add a backup agent that can upload backups to some local or remote location.
|
||||||
|
2. Pause or prepare integration operations before creating a backup and/or run some operation after a backup.
|
||||||
|
|
||||||
|
## Backup Agents
|
||||||
|
|
||||||
|
To add one or more backup agents, implement the two methods, `async_get_backup_agents` and `async_register_backup_agents_listener` in `backup.py`. Example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def async_get_backup_agents(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
) -> list[BackupAgent]:
|
||||||
|
"""Return a list of backup agents."""
|
||||||
|
if not hass.config_entries.async_loaded_entries(DOMAIN):
|
||||||
|
LOGGER.debug("No config entry found or entry is not loaded")
|
||||||
|
return []
|
||||||
|
return [ExampleBackupAgent()]
|
||||||
|
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def async_register_backup_agents_listener(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
*,
|
||||||
|
listener: Callable[[], None],
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> Callable[[], None]:
|
||||||
|
"""Register a listener to be called when agents are added or removed.
|
||||||
|
|
||||||
|
:return: A function to unregister the listener.
|
||||||
|
"""
|
||||||
|
hass.data.setdefault(DATA_BACKUP_AGENT_LISTENERS, []).append(listener)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def remove_listener() -> None:
|
||||||
|
"""Remove the listener."""
|
||||||
|
hass.data[DATA_BACKUP_AGENT_LISTENERS].remove(listener)
|
||||||
|
|
||||||
|
return remove_listener
|
||||||
|
```
|
||||||
|
|
||||||
|
The listener stored in `async_register_backup_agents_listener` should be called every time there is the need to reload backup agents, to remove stale agents and add new ones, such as when the integration is reloaded. For example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
|
"""Unload config entry."""
|
||||||
|
# Notify backup listeners
|
||||||
|
hass.async_create_task(_notify_backup_listeners(hass), eager_start=False)
|
||||||
|
|
||||||
|
return await hass.config_entries.async_unload_platforms(
|
||||||
|
entry, PLATFORMS
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _notify_backup_listeners(hass: HomeAssistant) -> None:
|
||||||
|
for listener in hass.data.get(DATA_BACKUP_AGENT_LISTENERS, []):
|
||||||
|
listener()
|
||||||
|
```
|
||||||
|
|
||||||
|
A backup agent should implement the abstract interface of the `BackupAgent` base class as shown in this example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from homeassistant.components.backup import BackupAgent, BackupAgentError
|
||||||
|
|
||||||
|
from .const import DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleBackupAgent(BackupAgent):
|
||||||
|
"""Backup agent interface."""
|
||||||
|
|
||||||
|
domain = DOMAIN
|
||||||
|
name = "example"
|
||||||
|
|
||||||
|
async def async_download_backup(
|
||||||
|
self,
|
||||||
|
backup_id: str,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> AsyncIterator[bytes]:
|
||||||
|
"""Download a backup file.
|
||||||
|
|
||||||
|
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
||||||
|
:return: An async iterator that yields bytes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def async_upload_backup(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
open_stream: Callable[[], Coroutine[Any, Any, AsyncIterator[bytes]]],
|
||||||
|
backup: AgentBackup,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Upload a backup.
|
||||||
|
|
||||||
|
:param open_stream: A function returning an async iterator that yields bytes.
|
||||||
|
:param backup: Metadata about the backup that should be uploaded.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def async_delete_backup(
|
||||||
|
self,
|
||||||
|
backup_id: str,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> None:
|
||||||
|
"""Delete a backup file.
|
||||||
|
|
||||||
|
:param backup_id: The ID of the backup that was returned in async_list_backups.
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def async_list_backups(self, **kwargs: Any) -> list[AgentBackup]:
|
||||||
|
"""List backups."""
|
||||||
|
|
||||||
|
async def async_get_backup(
|
||||||
|
self,
|
||||||
|
backup_id: str,
|
||||||
|
**kwargs: Any,
|
||||||
|
) -> AgentBackup | None:
|
||||||
|
"""Return a backup."""
|
||||||
|
```
|
||||||
|
|
||||||
|
Backup agents should raise a `BackupAgentError` exception on error. Other exceptions are not expected to leave the backup agent.
|
||||||
|
|
||||||
|
## Pre- and post-operations
|
||||||
|
|
||||||
When Home Assistant is creating a backup, there might be a need to pause certain operations in the integration, or dumping data so it can properly be restored.
|
When Home Assistant is creating a backup, there might be a need to pause certain operations in the integration, or dumping data so it can properly be restored.
|
||||||
|
|
||||||
This is done by adding 2 functions (`async_pre_backup` and `async_post_backup`) to `backup.py`
|
This is done by adding two functions (`async_pre_backup` and `async_post_backup`) to `backup.py`
|
||||||
|
|
||||||
## Adding support
|
### Adding support
|
||||||
|
|
||||||
The quickest way to add backup support to a new integration is by using our built-in scaffold template. From a Home Assistant dev environment, run `python3 -m script.scaffold backup` and follow the instructions.
|
The quickest way to add backup support to a new integration is by using our built-in scaffold template. From a Home Assistant dev environment, run `python3 -m script.scaffold backup` and follow the instructions.
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user