Example #1
0
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
Example #2
0
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
Example #3
0
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
Example #4
0
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