mirror of
https://github.com/CCOSTAN/Home-AssistantConfig.git
synced 2025-08-20 12:10:28 +00:00
Finally got around to installing This amazing component by @ludeeus
This commit is contained in:
386
config/custom_components/hacs/hacsbase/__init__.py
Executable file
386
config/custom_components/hacs/hacsbase/__init__.py
Executable file
@@ -0,0 +1,386 @@
|
||||
"""Initialize the HACS base."""
|
||||
# pylint: disable=unused-argument, bad-continuation
|
||||
import json
|
||||
import uuid
|
||||
from datetime import timedelta
|
||||
|
||||
from homeassistant.helpers.event import async_call_later, async_track_time_interval
|
||||
|
||||
from aiogithubapi import AIOGitHubException, AIOGitHubRatelimit
|
||||
from integrationhelper import Logger
|
||||
|
||||
from .task_factory import HacsTaskFactory
|
||||
from .exceptions import HacsException
|
||||
|
||||
from ..const import ELEMENT_TYPES
|
||||
from ..setup import setup_extra_stores
|
||||
from ..store import async_load_from_store, async_save_to_store
|
||||
from ..helpers.get_defaults import get_default_repos_lists, get_default_repos_orgs
|
||||
|
||||
|
||||
class HacsStatus:
|
||||
"""HacsStatus."""
|
||||
|
||||
startup = True
|
||||
new = False
|
||||
background_task = False
|
||||
reloading_data = False
|
||||
upgrading_all = False
|
||||
|
||||
|
||||
class HacsFrontend:
|
||||
"""HacsFrontend."""
|
||||
|
||||
version_running = None
|
||||
version_available = None
|
||||
update_pending = False
|
||||
|
||||
|
||||
class HacsCommon:
|
||||
"""Common for HACS."""
|
||||
|
||||
categories = []
|
||||
blacklist = []
|
||||
default = []
|
||||
installed = []
|
||||
skip = []
|
||||
|
||||
|
||||
class System:
|
||||
"""System info."""
|
||||
|
||||
status = HacsStatus()
|
||||
config_path = None
|
||||
ha_version = None
|
||||
disabled = False
|
||||
lovelace_mode = "storage"
|
||||
|
||||
|
||||
class Developer:
|
||||
"""Developer settings/tools."""
|
||||
|
||||
template_id = "Repository ID"
|
||||
template_content = ""
|
||||
template_raw = ""
|
||||
|
||||
@property
|
||||
def devcontainer(self):
|
||||
"""Is it a devcontainer?"""
|
||||
import os
|
||||
|
||||
if "DEVCONTAINER" in os.environ:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class Hacs:
|
||||
"""The base class of HACS, nested thoughout the project."""
|
||||
|
||||
token = f"{str(uuid.uuid4())}-{str(uuid.uuid4())}"
|
||||
hacsweb = f"/hacsweb/{token}"
|
||||
hacsapi = f"/hacsapi/{token}"
|
||||
repositories = []
|
||||
frontend = HacsFrontend()
|
||||
repo = None
|
||||
data_repo = None
|
||||
developer = Developer()
|
||||
data = None
|
||||
configuration = None
|
||||
logger = Logger("hacs")
|
||||
github = None
|
||||
hass = None
|
||||
version = None
|
||||
factory = HacsTaskFactory()
|
||||
system = System()
|
||||
recuring_tasks = []
|
||||
common = HacsCommon()
|
||||
|
||||
@staticmethod
|
||||
def init(hass, github_token):
|
||||
"""Return a initialized HACS object."""
|
||||
return Hacs()
|
||||
|
||||
def get_by_id(self, repository_id):
|
||||
"""Get repository by ID."""
|
||||
try:
|
||||
for repository in self.repositories:
|
||||
if repository.information.uid == repository_id:
|
||||
return repository
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
return None
|
||||
|
||||
def get_by_name(self, repository_full_name):
|
||||
"""Get repository by full_name."""
|
||||
try:
|
||||
for repository in self.repositories:
|
||||
if repository.information.full_name == repository_full_name:
|
||||
return repository
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
return None
|
||||
|
||||
def is_known(self, repository_full_name):
|
||||
"""Return a bool if the repository is known."""
|
||||
for repository in self.repositories:
|
||||
if repository.information.full_name == repository_full_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def sorted_by_name(self):
|
||||
"""Return a sorted(by name) list of repository objects."""
|
||||
return sorted(self.repositories, key=lambda x: x.display_name)
|
||||
|
||||
@property
|
||||
def sorted_by_repository_name(self):
|
||||
"""Return a sorted(by repository_name) list of repository objects."""
|
||||
return sorted(self.repositories, key=lambda x: x.information.full_name)
|
||||
|
||||
async def register_repository(self, full_name, category, check=True):
|
||||
"""Register a repository."""
|
||||
from ..repositories.repository import RERPOSITORY_CLASSES
|
||||
|
||||
if full_name in self.common.skip:
|
||||
if full_name != "hacs/integration":
|
||||
self.logger.debug(f"Skipping {full_name}")
|
||||
return
|
||||
|
||||
if category not in RERPOSITORY_CLASSES:
|
||||
msg = f"{category} is not a valid repository category."
|
||||
self.logger.error(msg)
|
||||
raise HacsException(msg)
|
||||
|
||||
repository = RERPOSITORY_CLASSES[category](full_name)
|
||||
if check:
|
||||
try:
|
||||
await repository.registration()
|
||||
if self.system.status.new:
|
||||
repository.status.new = False
|
||||
if repository.validate.errors:
|
||||
self.common.skip.append(repository.information.full_name)
|
||||
if not self.system.status.startup:
|
||||
self.logger.error(f"Validation for {full_name} failed.")
|
||||
return repository.validate.errors
|
||||
repository.logger.info("Registration complete")
|
||||
except AIOGitHubException as exception:
|
||||
self.logger.debug(self.github.ratelimits.remaining)
|
||||
self.logger.debug(self.github.ratelimits.reset_utc)
|
||||
self.common.skip.append(repository.information.full_name)
|
||||
# if not self.system.status.startup:
|
||||
if self.system.status.startup:
|
||||
self.logger.error(
|
||||
f"Validation for {full_name} failed with {exception}."
|
||||
)
|
||||
return exception
|
||||
self.hass.bus.async_fire(
|
||||
"hacs/repository",
|
||||
{
|
||||
"id": 1337,
|
||||
"action": "registration",
|
||||
"repository": repository.information.full_name,
|
||||
"repository_id": repository.information.uid,
|
||||
},
|
||||
)
|
||||
self.repositories.append(repository)
|
||||
|
||||
async def startup_tasks(self):
|
||||
"""Tasks tha are started after startup."""
|
||||
self.system.status.background_task = True
|
||||
await self.hass.async_add_executor_job(setup_extra_stores, self)
|
||||
self.hass.bus.async_fire("hacs/status", {})
|
||||
self.logger.debug(self.github.ratelimits.remaining)
|
||||
self.logger.debug(self.github.ratelimits.reset_utc)
|
||||
|
||||
await self.handle_critical_repositories_startup()
|
||||
await self.handle_critical_repositories()
|
||||
await self.load_known_repositories()
|
||||
await self.clear_out_blacklisted_repositories()
|
||||
|
||||
self.recuring_tasks.append(
|
||||
async_track_time_interval(
|
||||
self.hass, self.recuring_tasks_installed, timedelta(minutes=30)
|
||||
)
|
||||
)
|
||||
self.recuring_tasks.append(
|
||||
async_track_time_interval(
|
||||
self.hass, self.recuring_tasks_all, timedelta(minutes=800)
|
||||
)
|
||||
)
|
||||
|
||||
self.hass.bus.async_fire("hacs/reload", {"force": True})
|
||||
await self.recuring_tasks_installed()
|
||||
|
||||
self.system.status.startup = False
|
||||
self.system.status.new = False
|
||||
self.system.status.background_task = False
|
||||
self.hass.bus.async_fire("hacs/status", {})
|
||||
await self.data.async_write()
|
||||
|
||||
async def handle_critical_repositories_startup(self):
|
||||
"""Handled critical repositories during startup."""
|
||||
alert = False
|
||||
critical = await async_load_from_store(self.hass, "critical")
|
||||
if not critical:
|
||||
return
|
||||
for repo in critical:
|
||||
if not repo["acknowledged"]:
|
||||
alert = True
|
||||
if alert:
|
||||
self.logger.critical("URGENT!: Check the HACS panel!")
|
||||
self.hass.components.persistent_notification.create(
|
||||
title="URGENT!", message="**Check the HACS panel!**"
|
||||
)
|
||||
|
||||
async def handle_critical_repositories(self):
|
||||
"""Handled critical repositories during runtime."""
|
||||
# Get critical repositories
|
||||
instored = []
|
||||
critical = []
|
||||
was_installed = False
|
||||
|
||||
try:
|
||||
critical = await self.data_repo.get_contents("critical")
|
||||
critical = json.loads(critical.content)
|
||||
except AIOGitHubException:
|
||||
pass
|
||||
|
||||
if not critical:
|
||||
self.logger.debug("No critical repositories")
|
||||
return
|
||||
|
||||
stored_critical = await async_load_from_store(self.hass, "critical")
|
||||
|
||||
for stored in stored_critical or []:
|
||||
instored.append(stored["repository"])
|
||||
|
||||
stored_critical = []
|
||||
|
||||
for repository in critical:
|
||||
self.common.blacklist.append(repository["repository"])
|
||||
repo = self.get_by_name(repository["repository"])
|
||||
|
||||
stored = {
|
||||
"repository": repository["repository"],
|
||||
"reason": repository["reason"],
|
||||
"link": repository["link"],
|
||||
"acknowledged": True,
|
||||
}
|
||||
|
||||
if repository["repository"] not in instored:
|
||||
if repo is not None and repo.installed:
|
||||
self.logger.critical(
|
||||
f"Removing repository {repository['repository']}, it is marked as critical"
|
||||
)
|
||||
was_installed = True
|
||||
stored["acknowledged"] = False
|
||||
# Uninstall from HACS
|
||||
repo.remove()
|
||||
await repo.uninstall()
|
||||
stored_critical.append(stored)
|
||||
|
||||
# Save to FS
|
||||
await async_save_to_store(self.hass, "critical", stored_critical)
|
||||
|
||||
# Resart HASS
|
||||
if was_installed:
|
||||
self.logger.critical("Resarting Home Assistant")
|
||||
self.hass.async_create_task(self.hass.async_stop(100))
|
||||
|
||||
async def recuring_tasks_installed(self, notarealarg=None):
|
||||
"""Recuring tasks for installed repositories."""
|
||||
self.logger.debug(
|
||||
"Starting recuring background task for installed repositories"
|
||||
)
|
||||
self.system.status.background_task = True
|
||||
self.hass.bus.async_fire("hacs/status", {})
|
||||
self.logger.debug(self.github.ratelimits.remaining)
|
||||
self.logger.debug(self.github.ratelimits.reset_utc)
|
||||
for repository in self.repositories:
|
||||
if (
|
||||
repository.status.installed
|
||||
and repository.category in self.common.categories
|
||||
):
|
||||
self.factory.tasks.append(self.factory.safe_update(repository))
|
||||
|
||||
await self.factory.execute()
|
||||
await self.handle_critical_repositories()
|
||||
self.system.status.background_task = False
|
||||
self.hass.bus.async_fire("hacs/status", {})
|
||||
await self.data.async_write()
|
||||
self.logger.debug("Recuring background task for installed repositories done")
|
||||
|
||||
async def recuring_tasks_all(self, notarealarg=None):
|
||||
"""Recuring tasks for all repositories."""
|
||||
self.logger.debug("Starting recuring background task for all repositories")
|
||||
self.system.status.background_task = True
|
||||
self.hass.bus.async_fire("hacs/status", {})
|
||||
self.logger.debug(self.github.ratelimits.remaining)
|
||||
self.logger.debug(self.github.ratelimits.reset_utc)
|
||||
for repository in self.repositories:
|
||||
if repository.category in self.common.categories:
|
||||
self.factory.tasks.append(self.factory.safe_common_update(repository))
|
||||
|
||||
await self.factory.execute()
|
||||
await self.load_known_repositories()
|
||||
await self.clear_out_blacklisted_repositories()
|
||||
self.system.status.background_task = False
|
||||
await self.data.async_write()
|
||||
self.hass.bus.async_fire("hacs/status", {})
|
||||
self.hass.bus.async_fire("hacs/repository", {"action": "reload"})
|
||||
self.logger.debug("Recuring background task for all repositories done")
|
||||
|
||||
async def clear_out_blacklisted_repositories(self):
|
||||
"""Clear out blaclisted repositories."""
|
||||
need_to_save = False
|
||||
for repository in self.common.blacklist:
|
||||
if self.is_known(repository):
|
||||
repository = self.get_by_name(repository)
|
||||
if repository.status.installed:
|
||||
self.logger.warning(
|
||||
f"You have {repository.information.full_name} installed with HACS "
|
||||
+ "this repository has been blacklisted, please consider removing it."
|
||||
)
|
||||
else:
|
||||
need_to_save = True
|
||||
repository.remove()
|
||||
|
||||
if need_to_save:
|
||||
await self.data.async_write()
|
||||
|
||||
async def get_repositories(self):
|
||||
"""Return a list of repositories."""
|
||||
repositories = {}
|
||||
for category in self.common.categories:
|
||||
repositories[category] = await get_default_repos_lists(
|
||||
self.github, category
|
||||
)
|
||||
org = await get_default_repos_orgs(self.github, category)
|
||||
for repo in org:
|
||||
repositories[category].append(repo)
|
||||
|
||||
for category in repositories:
|
||||
for repo in repositories[category]:
|
||||
if repo not in self.common.default:
|
||||
self.common.default.append(repo)
|
||||
return repositories
|
||||
|
||||
async def load_known_repositories(self):
|
||||
"""Load known repositories."""
|
||||
self.logger.info("Loading known repositories")
|
||||
repositories = await self.get_repositories()
|
||||
|
||||
for item in await get_default_repos_lists(self.github, "blacklist"):
|
||||
if item not in self.common.blacklist:
|
||||
self.common.blacklist.append(item)
|
||||
|
||||
for category in repositories:
|
||||
for repo in repositories[category]:
|
||||
if repo in self.common.blacklist:
|
||||
continue
|
||||
if self.is_known(repo):
|
||||
continue
|
||||
self.factory.tasks.append(
|
||||
self.factory.safe_register(self, repo, category)
|
||||
)
|
||||
await self.factory.execute()
|
72
config/custom_components/hacs/hacsbase/backup.py
Executable file
72
config/custom_components/hacs/hacsbase/backup.py
Executable file
@@ -0,0 +1,72 @@
|
||||
"""Backup."""
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from time import sleep
|
||||
|
||||
from integrationhelper import Logger
|
||||
|
||||
BACKUP_PATH = tempfile.gettempdir() + "/hacs_backup/"
|
||||
|
||||
|
||||
class Backup:
|
||||
"""Backup."""
|
||||
|
||||
def __init__(self, local_path, backup_path=BACKUP_PATH):
|
||||
"""initialize."""
|
||||
self.logger = Logger("hacs.backup")
|
||||
self.local_path = local_path
|
||||
self.backup_path = backup_path
|
||||
self.backup_path_full = f"{self.backup_path}{self.local_path.split('/')[-1]}"
|
||||
|
||||
def create(self):
|
||||
"""Create a backup in /tmp"""
|
||||
if not os.path.exists(self.local_path):
|
||||
return
|
||||
if os.path.exists(self.backup_path):
|
||||
shutil.rmtree(self.backup_path)
|
||||
while os.path.exists(self.backup_path):
|
||||
sleep(0.1)
|
||||
os.makedirs(self.backup_path, exist_ok=True)
|
||||
|
||||
try:
|
||||
if os.path.isfile(self.local_path):
|
||||
shutil.copyfile(self.local_path, self.backup_path_full)
|
||||
os.remove(self.local_path)
|
||||
else:
|
||||
shutil.copytree(self.local_path, self.backup_path_full)
|
||||
shutil.rmtree(self.local_path)
|
||||
while os.path.exists(self.local_path):
|
||||
sleep(0.1)
|
||||
self.logger.debug(
|
||||
f"Backup for {self.local_path}, created in {self.backup_path_full}"
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
pass
|
||||
|
||||
def restore(self):
|
||||
"""Restore from backup."""
|
||||
if not os.path.exists(self.backup_path_full):
|
||||
return
|
||||
|
||||
if os.path.isfile(self.backup_path_full):
|
||||
if os.path.exists(self.local_path):
|
||||
os.remove(self.local_path)
|
||||
shutil.copyfile(self.backup_path_full, self.local_path)
|
||||
else:
|
||||
if os.path.exists(self.local_path):
|
||||
shutil.rmtree(self.local_path)
|
||||
while os.path.exists(self.local_path):
|
||||
sleep(0.1)
|
||||
shutil.copytree(self.backup_path_full, self.local_path)
|
||||
self.logger.debug(
|
||||
f"Restored {self.local_path}, from backup {self.backup_path_full}"
|
||||
)
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup backup files."""
|
||||
if os.path.exists(self.backup_path):
|
||||
shutil.rmtree(self.backup_path)
|
||||
while os.path.exists(self.backup_path):
|
||||
sleep(0.1)
|
||||
self.logger.debug(f"Backup dir {self.backup_path} cleared")
|
71
config/custom_components/hacs/hacsbase/configuration.py
Executable file
71
config/custom_components/hacs/hacsbase/configuration.py
Executable file
@@ -0,0 +1,71 @@
|
||||
"""HACS Configuration."""
|
||||
import attr
|
||||
from integrationhelper import Logger
|
||||
from custom_components.hacs.hacsbase.exceptions import HacsException
|
||||
|
||||
|
||||
@attr.s(auto_attribs=True)
|
||||
class Configuration:
|
||||
"""Configuration class."""
|
||||
|
||||
# Main configuration:
|
||||
appdaemon_path: str = "appdaemon/apps/"
|
||||
appdaemon: bool = False
|
||||
config: dict = {}
|
||||
config_entry: dict = {}
|
||||
config_type: str = None
|
||||
debug: bool = False
|
||||
dev: bool = False
|
||||
frontend_mode: str = "Grid"
|
||||
frontend_compact: bool = False
|
||||
options: dict = {}
|
||||
onboarding_done: bool = False
|
||||
plugin_path: str = "www/community/"
|
||||
python_script_path: str = "python_scripts/"
|
||||
python_script: bool = False
|
||||
sidepanel_icon: str = "mdi:alpha-c-box"
|
||||
sidepanel_title: str = "Community"
|
||||
theme_path: str = "themes/"
|
||||
theme: bool = False
|
||||
token: str = None
|
||||
|
||||
# Config options:
|
||||
country: str = "ALL"
|
||||
experimental: bool = False
|
||||
release_limit: int = 5
|
||||
|
||||
def to_json(self):
|
||||
"""Return a dict representation of the configuration."""
|
||||
return self.__dict__
|
||||
|
||||
def print(self):
|
||||
"""Print the current configuration to the log."""
|
||||
logger = Logger("hacs.configuration")
|
||||
config = self.to_json()
|
||||
for key in config:
|
||||
if key in ["config", "config_entry", "options", "token"]:
|
||||
continue
|
||||
logger.debug(f"{key}: {config[key]}")
|
||||
|
||||
@staticmethod
|
||||
def from_dict(configuration: dict, options: dict):
|
||||
"""Set attributes from dicts."""
|
||||
if isinstance(options, bool) or isinstance(configuration.get("options"), bool):
|
||||
raise HacsException("Configuration is not valid.")
|
||||
|
||||
if options is None:
|
||||
options = {}
|
||||
|
||||
if not configuration:
|
||||
raise HacsException("Configuration is not valid.")
|
||||
|
||||
config = Configuration()
|
||||
|
||||
config.config = configuration
|
||||
config.options = options
|
||||
|
||||
for conf_type in [configuration, options]:
|
||||
for key in conf_type:
|
||||
setattr(config, key, conf_type[key])
|
||||
|
||||
return config
|
10
config/custom_components/hacs/hacsbase/const.py
Executable file
10
config/custom_components/hacs/hacsbase/const.py
Executable file
@@ -0,0 +1,10 @@
|
||||
"""Constants for HACS"""
|
||||
# pylint: disable=unused-import
|
||||
STORAGE_VERSION = "6"
|
||||
STORENAME = "hacs"
|
||||
|
||||
# Messages
|
||||
NOT_SUPPORTED_HA_VERSION = "You have version '{}' of Home Assistant, but version '{}' of '{}' require version '{}' of Home Assistant, install and upgrades are disabled for this integration untill you upgrade Home Assistant."
|
||||
|
||||
|
||||
NO_ELEMENTS = "No elements to show, open the store to install some awesome stuff."
|
144
config/custom_components/hacs/hacsbase/data.py
Executable file
144
config/custom_components/hacs/hacsbase/data.py
Executable file
@@ -0,0 +1,144 @@
|
||||
"""Data handler for HACS."""
|
||||
from integrationhelper import Logger
|
||||
from . import Hacs
|
||||
from ..const import VERSION
|
||||
from ..repositories.repository import HacsRepository
|
||||
from ..repositories.manifest import HacsManifest
|
||||
from ..store import async_save_to_store, async_load_from_store
|
||||
|
||||
|
||||
class HacsData(Hacs):
|
||||
"""HacsData class."""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize."""
|
||||
self.logger = Logger("hacs.data")
|
||||
|
||||
async def async_write(self):
|
||||
"""Write content to the store files."""
|
||||
if self.system.status.background_task or self.system.disabled:
|
||||
return
|
||||
|
||||
self.logger.debug("Saving data")
|
||||
|
||||
# Hacs
|
||||
await async_save_to_store(
|
||||
self.hass,
|
||||
"hacs",
|
||||
{
|
||||
"view": self.configuration.frontend_mode,
|
||||
"compact": self.configuration.frontend_compact,
|
||||
"onboarding_done": self.configuration.onboarding_done,
|
||||
},
|
||||
)
|
||||
|
||||
# Repositories
|
||||
content = {}
|
||||
for repository in self.repositories:
|
||||
if repository.repository_manifest is not None:
|
||||
repository_manifest = repository.repository_manifest.manifest
|
||||
else:
|
||||
repository_manifest = None
|
||||
content[repository.information.uid] = {
|
||||
"authors": repository.information.authors,
|
||||
"category": repository.information.category,
|
||||
"description": repository.information.description,
|
||||
"downloads": repository.releases.last_release_object_downloads,
|
||||
"full_name": repository.information.full_name,
|
||||
"first_install": repository.status.first_install,
|
||||
"hide": repository.status.hide,
|
||||
"installed_commit": repository.versions.installed_commit,
|
||||
"installed": repository.status.installed,
|
||||
"last_commit": repository.versions.available_commit,
|
||||
"last_release_tag": repository.versions.available,
|
||||
"last_updated": repository.information.last_updated,
|
||||
"name": repository.information.name,
|
||||
"new": repository.status.new,
|
||||
"repository_manifest": repository_manifest,
|
||||
"selected_tag": repository.status.selected_tag,
|
||||
"show_beta": repository.status.show_beta,
|
||||
"stars": repository.information.stars,
|
||||
"topics": repository.information.topics,
|
||||
"version_installed": repository.versions.installed,
|
||||
}
|
||||
|
||||
await async_save_to_store(self.hass, "repositories", content)
|
||||
self.hass.bus.async_fire("hacs/repository", {})
|
||||
self.hass.bus.fire("hacs/config", {})
|
||||
|
||||
async def restore(self):
|
||||
"""Restore saved data."""
|
||||
hacs = await async_load_from_store(self.hass, "hacs")
|
||||
repositories = await async_load_from_store(self.hass, "repositories")
|
||||
try:
|
||||
if not hacs and not repositories:
|
||||
# Assume new install
|
||||
self.system.status.new = True
|
||||
return True
|
||||
self.logger.info("Restore started")
|
||||
|
||||
# Hacs
|
||||
self.configuration.frontend_mode = hacs.get("view", "Grid")
|
||||
self.configuration.frontend_compact = hacs.get("compact", False)
|
||||
self.configuration.onboarding_done = hacs.get("onboarding_done", False)
|
||||
|
||||
# Repositories
|
||||
for entry in repositories:
|
||||
repo = repositories[entry]
|
||||
if repo["full_name"] == "hacs/integration":
|
||||
# Skip the old repo location
|
||||
continue
|
||||
if not self.is_known(repo["full_name"]):
|
||||
await self.register_repository(
|
||||
repo["full_name"], repo["category"], False
|
||||
)
|
||||
repository = self.get_by_name(repo["full_name"])
|
||||
if repository is None:
|
||||
self.logger.error(f"Did not find {repo['full_name']}")
|
||||
continue
|
||||
|
||||
# Restore repository attributes
|
||||
repository.information.uid = entry
|
||||
await self.hass.async_add_executor_job(
|
||||
restore_repository_data, repository, repo
|
||||
)
|
||||
|
||||
self.logger.info("Restore done")
|
||||
except Exception as exception: # pylint: disable=broad-except
|
||||
self.logger.critical(f"[{exception}] Restore Failed!")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def restore_repository_data(
|
||||
repository: type(HacsRepository), repository_data: dict
|
||||
) -> None:
|
||||
"""Restore Repository Data"""
|
||||
repository.information.authors = repository_data.get("authors", [])
|
||||
repository.information.description = repository_data.get("description")
|
||||
repository.information.name = repository_data.get("name")
|
||||
repository.releases.last_release_object_downloads = repository_data.get("downloads")
|
||||
repository.information.last_updated = repository_data.get("last_updated")
|
||||
repository.information.topics = repository_data.get("topics", [])
|
||||
repository.information.stars = repository_data.get("stars", 0)
|
||||
repository.releases.last_release = repository_data.get("last_release_tag")
|
||||
repository.status.hide = repository_data.get("hide", False)
|
||||
repository.status.installed = repository_data.get("installed", False)
|
||||
repository.status.new = repository_data.get("new", True)
|
||||
repository.status.selected_tag = repository_data.get("selected_tag")
|
||||
repository.status.show_beta = repository_data.get("show_beta", False)
|
||||
repository.versions.available = repository_data.get("last_release_tag")
|
||||
repository.versions.available_commit = repository_data.get("last_commit")
|
||||
repository.versions.installed = repository_data.get("version_installed")
|
||||
repository.versions.installed_commit = repository_data.get("installed_commit")
|
||||
|
||||
repository.repository_manifest = HacsManifest.from_dict(
|
||||
repository_data.get("repository_manifest", {})
|
||||
)
|
||||
|
||||
if repository.status.installed:
|
||||
repository.status.first_install = False
|
||||
|
||||
if repository_data["full_name"] == "hacs/integration":
|
||||
repository.versions.installed = VERSION
|
||||
repository.status.installed = True
|
5
config/custom_components/hacs/hacsbase/exceptions.py
Executable file
5
config/custom_components/hacs/hacsbase/exceptions.py
Executable file
@@ -0,0 +1,5 @@
|
||||
"""Custom Exceptions."""
|
||||
|
||||
|
||||
class HacsException(Exception):
|
||||
"""Super basic."""
|
71
config/custom_components/hacs/hacsbase/task_factory.py
Executable file
71
config/custom_components/hacs/hacsbase/task_factory.py
Executable file
@@ -0,0 +1,71 @@
|
||||
# pylint: disable=missing-docstring,invalid-name
|
||||
import logging
|
||||
import time
|
||||
from datetime import timedelta
|
||||
import asyncio
|
||||
from aiogithubapi import AIOGitHubException
|
||||
|
||||
max_concurrent_tasks = asyncio.Semaphore(15)
|
||||
sleeper = 5
|
||||
|
||||
logger = logging.getLogger("hacs.factory")
|
||||
|
||||
|
||||
class HacsTaskFactory:
|
||||
def __init__(self):
|
||||
self.tasks = []
|
||||
self.running = False
|
||||
|
||||
async def execute(self):
|
||||
if not self.tasks:
|
||||
logger.debug("No tasks to execute")
|
||||
return
|
||||
if self.running:
|
||||
logger.debug("Allready executing tasks")
|
||||
return
|
||||
try:
|
||||
self.running = True
|
||||
logger.info("Processing %s tasks", len(self.tasks))
|
||||
start = time.time()
|
||||
await asyncio.gather(*self.tasks)
|
||||
logger.info(
|
||||
"Task processing of %s tasks completed in %s seconds",
|
||||
len(self.tasks),
|
||||
timedelta(seconds=round(time.time() - start)).seconds,
|
||||
)
|
||||
self.tasks = []
|
||||
self.running = False
|
||||
except RuntimeError:
|
||||
logger.warning("RuntimeError, Clearing current tasks")
|
||||
self.tasks = []
|
||||
self.running = False
|
||||
|
||||
async def safe_common_update(self, repository):
|
||||
async with max_concurrent_tasks:
|
||||
try:
|
||||
await repository.common_update()
|
||||
except AIOGitHubException as exception:
|
||||
logger.error(exception)
|
||||
|
||||
# Due to GitHub ratelimits we need to sleep a bit
|
||||
await asyncio.sleep(sleeper)
|
||||
|
||||
async def safe_update(self, repository):
|
||||
async with max_concurrent_tasks:
|
||||
try:
|
||||
await repository.update_repository()
|
||||
except AIOGitHubException as exception:
|
||||
logger.error(exception)
|
||||
|
||||
# Due to GitHub ratelimits we need to sleep a bit
|
||||
await asyncio.sleep(sleeper)
|
||||
|
||||
async def safe_register(self, hacs, repo, category):
|
||||
async with max_concurrent_tasks:
|
||||
try:
|
||||
await hacs.register_repository(repo, category)
|
||||
except AIOGitHubException as exception:
|
||||
logger.error(exception)
|
||||
|
||||
# Due to GitHub ratelimits we need to sleep a bit
|
||||
await asyncio.sleep(sleeper)
|
Reference in New Issue
Block a user