class HacsData(Hacs): """HacsData class.""" def __init__(self): """Initialize.""" self.logger = Logger("hacs.data") def check_corrupted_files(self): """Return True if one (or more) of the files are corrupted.""" for store in STORES: path = f"{self.system.config_path}/.storage/{STORES[store]}" if os.path.exists(path): if os.stat(path).st_size == 0: # File is empty (corrupted) return True return False def read(self, store): """Return data from a store.""" path = f"{self.system.config_path}/.storage/{STORES[store]}" content = None if os.path.exists(path): with open(path, "r", encoding="utf-8") as storefile: content = storefile.read() content = json.loads(content) return content def write(self): """Write content to the store files.""" if self.system.status.background_task: return self.logger.debug("Saving data") # Hacs path = f"{self.system.config_path}/.storage/{STORES['hacs']}" hacs = {"view": self.configuration.frontend_mode} save(path, hacs) # Installed path = f"{self.system.config_path}/.storage/{STORES['installed']}" installed = {} for repository in self.common.installed: repository = self.get_by_name(repository) installed[repository.information.full_name] = { "version_type": repository.display_version_or_commit, "version_installed": repository.display_installed_version, "version_available": repository.display_available_version, } save(path, installed) # Repositories path = f"{self.system.config_path}/.storage/{STORES['repositories']}" content = {} for repository in self.repositories: content[repository.information.uid] = { "authors": repository.information.authors, "category": repository.information.category, "description": repository.information.description, "full_name": repository.information.full_name, "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, "name": repository.information.name, "new": repository.status.new, "selected_tag": repository.status.selected_tag, "show_beta": repository.status.show_beta, "version_installed": repository.versions.installed, } # Validate installed repositories count_installed = len(installed) + 1 # For HACS it self count_installed_restore = 0 for repository in self.repositories: if repository.status.installed: count_installed_restore += 1 if count_installed < count_installed_restore: self.logger.debug("Save failed!") self.logger.debug( f"Number of installed repositories does not match the number of stored repositories [{count_installed} vs {count_installed_restore}]" ) return save(path, content) async def restore(self): """Restore saved data.""" try: hacs = self.read("hacs") installed = self.read("installed") repositrories = self.read("repositories") if self.check_corrupted_files(): # Coruptted installation self.logger.critical( "Restore failed one or more files are corrupted!") return False if hacs is None and installed is None and repositrories is None: # Assume new install return True self.logger.info("Restore started") # Hacs hacs = hacs["data"] self.configuration.frontend_mode = hacs["view"] # Installed installed = installed["data"] for repository in installed: self.common.installed.append(repository) # Repositories repositrories = repositrories["data"] for entry in repositrories: repo = repositrories[entry] 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 if repo.get("authors") is not None: repository.information.authors = repo["authors"] if repo.get("description") is not None: repository.information.description = repo["description"] if repo.get("name") is not None: repository.information.name = repo["name"] if repo.get("hide") is not None: repository.status.hide = repo["hide"] if repo.get("installed") is not None: repository.status.installed = repo["installed"] if repository.status.installed: repository.status.first_install = False if repo.get("selected_tag") is not None: repository.status.selected_tag = repo["selected_tag"] if repo.get("show_beta") is not None: repository.status.show_beta = repo["show_beta"] if repo.get("last_commit") is not None: repository.versions.available_commit = repo["last_commit"] repository.information.uid = entry if repo.get("last_release_tag") is not None: repository.releases.last_release = repo["last_release_tag"] repository.versions.available = repo["last_release_tag"] if repo.get("new") is not None: repository.status.new = repo["new"] if repo["full_name"] == "custom-components/hacs": repository.versions.installed = VERSION if "b" in VERSION: repository.status.show_beta = True elif repo.get("version_installed") is not None: repository.versions.installed = repo["version_installed"] if repo.get("installed_commit") is not None: repository.versions.installed_commit = repo[ "installed_commit"] if repo["full_name"] in self.common.installed: repository.status.installed = True repository.status.new = False frominstalled = installed[repo["full_name"]] if frominstalled["version_type"] == "commit": repository.versions.installed_commit = frominstalled[ "version_installed"] repository.versions.available_commit = frominstalled[ "version_available"] else: repository.versions.installed = frominstalled[ "version_installed"] repository.versions.available = frominstalled[ "version_available"] # Check the restore. count_installed = len(installed) + 1 # For HACS it self count_installed_restore = 0 installed_restore = [] for repository in self.repositories: if repository.status.installed: installed_restore.append(repository.information.full_name) if (repository.information.full_name not in self.common.installed and repository.information.full_name != "custom-components/hacs"): self.logger.warning( f"{repository.information.full_name} is not in common.installed" ) count_installed_restore += 1 if count_installed < count_installed_restore: for repo in installed: installed_restore.remove(repo) self.logger.warning(f"Check {repo}") self.logger.critical("Restore failed!") self.logger.critical( f"Number of installed repositories does not match the number of restored repositories [{count_installed} vs {count_installed_restore}]" ) return False self.logger.info("Restore done") except Exception as exception: self.logger.critical(f"[{exception}] Restore Failed!") return False return True
class HacsData: """HacsData class.""" def __init__(self): """Initialize.""" self.logger = Logger("hacs.data") self.hacs = get_hacs() async def async_write(self): """Write content to the store files.""" if self.hacs.system.status.background_task or self.hacs.system.disabled: return self.logger.debug("Saving data") # Hacs await async_save_to_store( self.hacs.hass, "hacs", { "view": self.hacs.configuration.frontend_mode, "compact": self.hacs.configuration.frontend_compact, "onboarding_done": self.hacs.configuration.onboarding_done, }, ) # Repositories content = {} for repository in self.hacs.repositories: if repository.repository_manifest is not None: repository_manifest = repository.repository_manifest.manifest else: repository_manifest = None data = { "authors": repository.data.authors, "category": repository.data.category, "description": repository.data.description, "domain": repository.data.domain, "downloads": repository.data.downloads, "full_name": repository.data.full_name, "first_install": repository.status.first_install, "installed_commit": repository.data.installed_commit, "installed": repository.data.installed, "last_commit": repository.data.last_commit, "last_release_tag": repository.data.last_version, "last_updated": repository.data.last_updated, "name": repository.data.name, "new": repository.data.new, "repository_manifest": repository_manifest, "selected_tag": repository.data.selected_tag, "show_beta": repository.data.show_beta, "stars": repository.data.stargazers_count, "topics": repository.data.topics, "version_installed": repository.data.installed_version, } if data: if repository.data.installed and ( repository.data.installed_commit or repository.data.installed_version): await async_save_to_store( self.hacs.hass, f"hacs/{repository.data.id}.hacs", repository.data.to_json(), ) content[str(repository.data.id)] = data await async_save_to_store(self.hacs.hass, "repositories", content) self.hacs.hass.bus.async_fire("hacs/repository", {}) self.hacs.hass.bus.fire("hacs/config", {}) async def restore(self): """Restore saved data.""" hacs = await async_load_from_store(self.hacs.hass, "hacs") repositories = await async_load_from_store(self.hacs.hass, "repositories") try: if not hacs and not repositories: # Assume new install self.hacs.system.status.new = True return True self.logger.info("Restore started") self.hacs.system.status.new = False # Hacs self.hacs.configuration.frontend_mode = hacs.get("view", "Grid") self.hacs.configuration.frontend_compact = hacs.get( "compact", False) self.hacs.configuration.onboarding_done = hacs.get( "onboarding_done", False) # Repositories for entry in repositories: repo = repositories[entry] if not self.hacs.is_known(entry): await register_repository(repo["full_name"], repo["category"], False) repository = [ x for x in self.hacs.repositories if str(x.data.id) == str(entry) or x.data.full_name == repo["full_name"] ] if not repository: self.logger.error( f"Did not find {repo['full_name']} ({entry})") continue repository = repository[0] # Restore repository attributes repository.data.id = entry await self.hacs.hass.async_add_executor_job( restore_repository_data, repository, repo) restored = await async_load_from_store(self.hacs.hass, f"hacs/{entry}.hacs") if restored: repository.data.update_data(restored) if not repository.data.installed: repository.logger.debug( "Should be installed but is not... Fixing that!") repository.data.installed = True self.logger.info("Restore done") except Exception as exception: # pylint: disable=broad-except self.logger.critical(f"[{exception}] Restore Failed!") return False return True
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
class HacsData(Hacs): """HacsData class.""" def __init__(self): """Initialize.""" self.logger = Logger("hacs.data") def check_corrupted_files(self): """Return True if one (or more) of the files are corrupted.""" for store in STORES: path = f"{self.system.config_path}/.storage/{STORES[store]}" if os.path.exists(path): if os.stat(path).st_size == 0: # File is empty (corrupted) return True return False def read(self, store): """Return data from a store.""" path = f"{self.system.config_path}/.storage/{STORES[store]}" content = None if os.path.exists(path): with open(path, "r", encoding="utf-8") as storefile: content = storefile.read() content = json.loads(content) return content async def async_write(self): """Write content to the store files.""" if self.system.status.background_task: return self.logger.debug("Saving data") # Hacs await async_save_to_store(self.hass, "hacs", {"view": self.configuration.frontend_mode}) # 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, "topics": repository.information.topics, "category": repository.information.category, "description": repository.information.description, "full_name": repository.information.full_name, "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, "repository_manifest": repository_manifest, "name": repository.information.name, "new": repository.status.new, "selected_tag": repository.status.selected_tag, "show_beta": repository.status.show_beta, "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 = {} repositories = {} try: hacs = await async_load_from_store(self.hass, "hacs") except KeyError: await async_save_to_store(self.hass, "hacs", self.data.read("hacs")["data"]) hacs = await async_load_from_store(self.hass, "hacs") try: repositories = await async_load_from_store(self.hass, "repositories") except KeyError: await async_save_to_store(self.hass, "repositories", self.data.read("repositories")["data"]) repositories = await async_load_from_store(self.hass, "repositories") try: if self.check_corrupted_files(): # Coruptted installation self.logger.critical( "Restore failed one or more files are corrupted!") return False if hacs is None and repositories is None: # Assume new install return True self.logger.info("Restore started") # Hacs self.configuration.frontend_mode = hacs.get("view", "Grid") # Repositories repositories = repositories for entry in repositories: repo = repositories[entry] if repo["full_name"] == "custom-components/hacs": # 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 if repo.get("authors") is not None: repository.information.authors = repo["authors"] if repo.get("topics", []): repository.information.topics = repo["topics"] if repo.get("description") is not None: repository.information.description = repo["description"] if repo.get("name") is not None: repository.information.name = repo["name"] if repo.get("hide") is not None: repository.status.hide = repo["hide"] if repo.get("installed") is not None: repository.status.installed = repo["installed"] if repository.status.installed: repository.status.first_install = False if repo.get("selected_tag") is not None: repository.status.selected_tag = repo["selected_tag"] if repo.get("repository_manifest") is not None: repository.repository_manifest = HacsManifest.from_dict( repo["repository_manifest"]) if repo.get("show_beta") is not None: repository.status.show_beta = repo["show_beta"] if repo.get("last_commit") is not None: repository.versions.available_commit = repo["last_commit"] repository.information.uid = entry if repo.get("last_release_tag") is not None: repository.releases.last_release = repo["last_release_tag"] repository.versions.available = repo["last_release_tag"] if repo.get("new") is not None: repository.status.new = repo["new"] if repo["full_name"] == "hacs/integration": repository.versions.installed = VERSION repository.status.installed = True if "b" in VERSION: repository.status.show_beta = True elif repo.get("version_installed") is not None: repository.versions.installed = repo["version_installed"] if repo.get("installed_commit") is not None: repository.versions.installed_commit = repo[ "installed_commit"] self.logger.info("Restore done") except Exception as exception: self.logger.critical( f"[{exception}] Restore Failed! see https://github.com/hacs/integration/issues/639 for more details." ) return False return True