Download scripts/backup-homeassistant.py from YYP0502/Bot: direct link, hf CLI and curl.
- Browser
- Download file 3.43 kB
-
https://huggingface.co/spaces/YYP0502/Bot/resolve/main/scripts/backup-homeassistant.py
- Command line
-
hf download hf://spaces/YYP0502/Bot/scripts/backup-homeassistant.py
-
curl -L -o backup-homeassistant.py https://huggingface.co/spaces/YYP0502/Bot/resolve/main/scripts/backup-homeassistant.py
3.43 kB
| """Restore and upload a compact Home Assistant configuration snapshot.""" | |
| from __future__ import annotations | |
| import argparse | |
| import os | |
| import sys | |
| import tarfile | |
| import tempfile | |
| from pathlib import Path | |
| ARCHIVE_NAME = "homeassistant-latest.tar.gz" | |
| def hub_api(): | |
| from huggingface_hub import HfApi | |
| token = os.environ.get("HF_TOKEN") | |
| repo_id = os.environ.get("BACKUP_REPO_ID") | |
| if not token or not repo_id: | |
| raise RuntimeError("HF_TOKEN and BACKUP_REPO_ID are required for Hub backups") | |
| api = HfApi(token=token) | |
| repo_type = os.environ.get("BACKUP_REPO_TYPE", "dataset") | |
| try: | |
| api.repo_info(repo_id=repo_id, repo_type=repo_type) | |
| except Exception: | |
| api.create_repo(repo_id=repo_id, repo_type=repo_type, private=True, exist_ok=True) | |
| return api, repo_id, repo_type, token | |
| def archive_config(config: Path, archive: Path) -> None: | |
| with tarfile.open(archive, "w:gz") as tar: | |
| for path in config.rglob("*"): | |
| if not path.is_file() or path.is_symlink(): continue | |
| relative = path.relative_to(config).as_posix() | |
| if relative in {"home-assistant_v2.db", "home-assistant.log"}: continue | |
| if relative.endswith((".db-wal", ".db-shm")): continue | |
| if relative.startswith((".cloud/", "tts/")): continue | |
| tar.add(path, arcname=relative, recursive=False) | |
| def backup(config: Path) -> None: | |
| api, repo_id, repo_type, token = hub_api() | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| archive = Path(temp_dir) / ARCHIVE_NAME | |
| archive_config(config, archive) | |
| api.upload_file(path_or_fileobj=str(archive), path_in_repo=ARCHIVE_NAME, | |
| repo_id=repo_id, repo_type=repo_type, token=token, | |
| commit_message="Update Home Assistant configuration backup") | |
| print(f"Uploaded {ARCHIVE_NAME} to {repo_type}/{repo_id}", flush=True) | |
| def restore(config: Path) -> None: | |
| from huggingface_hub.errors import EntryNotFoundError | |
| try: | |
| _, repo_id, repo_type, token = hub_api() | |
| from huggingface_hub import hf_hub_download | |
| archive = hf_hub_download(repo_id=repo_id, filename=ARCHIVE_NAME, | |
| repo_type=repo_type, token=token, force_download=True) | |
| except EntryNotFoundError: | |
| print("No existing Hub snapshot; starting a new configuration.", flush=True) | |
| return | |
| except Exception as exc: | |
| raise RuntimeError("Hub restore failed; refusing to overwrite the remote backup with a fresh configuration.") from exc | |
| config.mkdir(parents=True, exist_ok=True) | |
| with tarfile.open(archive, "r:gz") as tar: | |
| root = config.resolve() | |
| for member in tar.getmembers(): | |
| target = (config / member.name).resolve() | |
| if root not in target.parents: raise RuntimeError(f"Unsafe backup path: {member.name}") | |
| tar.extractall(config, filter="data") | |
| print(f"Restored {ARCHIVE_NAME} into {config}", flush=True) | |
| def main() -> int: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("action", choices=("backup", "restore")) | |
| parser.add_argument("--config", required=True, type=Path) | |
| args = parser.parse_args() | |
| if not os.environ.get("HF_TOKEN") or not os.environ.get("BACKUP_REPO_ID"): return 0 | |
| (backup if args.action == "backup" else restore)(args.config) | |
| return 0 | |
| if __name__ == "__main__": raise SystemExit(main()) |