Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions custom_components/hacs/repositories/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@ async def _async_post_install(self) -> None:
"repository_id": self.data.id,
},
)
self.hacs.data.async_schedule_write()
self.logger.info("%s Post installation steps completed", self.string)

async def async_install_repository(self, *, version: str | None = None, **_) -> None:
Expand Down
96 changes: 59 additions & 37 deletions custom_components/hacs/utils/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
from ..repositories.base import TOPIC_FILTER, HacsManifest, HacsRepository
from .logger import LOGGER
from .path import is_safe
from .store import async_load_from_store, async_save_to_store
from .store import async_delay_save_to_store, async_load_from_store, async_save_to_store

DELAYED_WRITE_DELAY = 30

EXPORTED_BASE_DATA = (
("new", False),
Expand Down Expand Up @@ -62,7 +64,7 @@ def __init__(self, hacs: HacsBase):
"""Initialize."""
self.logger = LOGGER
self.hacs = hacs
self.content = {}
self.content: dict = {}

async def async_force_write(self, _=None):
"""Force write."""
Expand All @@ -75,45 +77,67 @@ async def async_write(self, force: bool = False) -> None:

self.logger.debug("<HacsData async_write> Saving data")

# Hacs
await async_save_to_store(
self.hacs.hass,
"hacs",
{
"archived_repositories": self.hacs.common.archived_repositories,
"renamed_repositories": self.hacs.common.renamed_repositories,
"ignored_repositories": self.hacs.common.ignored_repositories,
},
)
await self._async_store_experimental_content_and_repos()
await self._async_store_content_and_repos()
await async_save_to_store(self.hacs.hass, "hacs", self._build_hacs_data())
await async_save_to_store(self.hacs.hass, "data", self._build_experimental_data())
await async_save_to_store(self.hacs.hass, "repositories", self._build_repositories_data())
for event in (HacsDispatchEvent.REPOSITORY, HacsDispatchEvent.CONFIG):
self.hacs.async_dispatch(event, {})

async def _async_store_content_and_repos(self, _=None): # bb: ignore
"""Store the main repos file and each repo that is out of date."""
# Repositories
self.content = {}
for repository in self.hacs.repositories.list_all:
if repository.data.category in self.hacs.common.categories:
self.async_store_repository_data(repository)
@callback
def async_schedule_write(self) -> None:
"""Schedule a debounced write of all HACS stores."""
if self.hacs.system.disabled:
return

self.logger.debug("<HacsData async_schedule_write> Scheduling delayed save")

await async_save_to_store(self.hacs.hass, "repositories", self.content)
async_delay_save_to_store(
self.hacs.hass, "hacs", self._build_hacs_data, DELAYED_WRITE_DELAY
)
async_delay_save_to_store(
self.hacs.hass, "data", self._build_experimental_data, DELAYED_WRITE_DELAY
)
async_delay_save_to_store(
self.hacs.hass,
"repositories",
self._build_repositories_data,
DELAYED_WRITE_DELAY,
)
Comment thread
ludeeus marked this conversation as resolved.
for event in (HacsDispatchEvent.REPOSITORY, HacsDispatchEvent.CONFIG):
self.hacs.async_dispatch(event, {})

async def _async_store_experimental_content_and_repos(self, _=None):
"""Store the main repos file and each repo that is out of date."""
# Repositories
self.content = {}
@callback
def _build_hacs_data(self) -> dict:
return {
"archived_repositories": self.hacs.common.archived_repositories,
"renamed_repositories": self.hacs.common.renamed_repositories,
"ignored_repositories": self.hacs.common.ignored_repositories,
}

@callback
def _build_repositories_data(self) -> dict:
content: dict[str, dict] = {}
for repository in self.hacs.repositories.list_all:
if repository.data.category in self.hacs.common.categories:
self.async_store_experimental_repository_data(repository)
content[str(repository.data.id)] = self._repository_export(repository)
return content

await async_save_to_store(self.hacs.hass, "data", {"repositories": self.content})
@callback
def _build_experimental_data(self) -> dict:
content: dict[str, list] = {}
for repository in self.hacs.repositories.list_all:
if repository.data.category in self.hacs.common.categories:
content.setdefault(repository.data.category, []).append(
{
"id": str(repository.data.id),
**self._experimental_repository_export(repository),
}
)
return {"repositories": content}

@callback
def async_store_repository_data(self, repository: HacsRepository) -> dict:
"""Store the repository data."""
data = {"repository_manifest": repository.repository_manifest.manifest}
def _repository_export(self, repository: HacsRepository) -> dict:
data: dict[str, Any] = {"repository_manifest": repository.repository_manifest.manifest}

for key, default in (
EXPORTED_DOWNLOADED_REPOSITORY_DATA
Expand All @@ -128,13 +152,11 @@ def async_store_repository_data(self, repository: HacsRepository) -> dict:
if repository.data.last_fetched:
data["last_fetched"] = repository.data.last_fetched.timestamp()

self.content[str(repository.data.id)] = data
return data

@callback
def async_store_experimental_repository_data(self, repository: HacsRepository) -> None:
"""Store the experimental repository data for non downloaded repositories."""
data = {}
self.content.setdefault(repository.data.category, [])
def _experimental_repository_export(self, repository: HacsRepository) -> dict:
data: dict[str, Any] = {}

if repository.data.installed:
data["repository_manifest"] = repository.repository_manifest.manifest
Expand All @@ -151,7 +173,7 @@ def async_store_experimental_repository_data(self, repository: HacsRepository) -
if (value := getattr(repository.data, key, default)) != default:
data[key] = value

self.content[repository.data.category].append({"id": str(repository.data.id), **data})
return data

async def restore(self):
"""Restore saved data."""
Expand Down
15 changes: 15 additions & 0 deletions custom_components/hacs/utils/store.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"""Storage handers."""

from collections.abc import Callable
from typing import Any

from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.json import JSONEncoder
from homeassistant.helpers.storage import Store
from homeassistant.util import json as json_util
Expand Down Expand Up @@ -72,6 +76,17 @@ async def async_save_to_store(hass, key, data):
)


@callback
def async_delay_save_to_store(
hass: HomeAssistant,
key: str,
data_func: Callable[[], Any],
delay: float,
) -> None:
"""Schedule a debounced save to the store."""
get_store_for_key(hass, key).async_delay_save(data_func, delay)
Comment thread
ludeeus marked this conversation as resolved.


async def async_remove_store(hass, key):
"""Remove a store element that should no longer be used."""
if "/" not in key:
Expand Down
Loading