YYP0502 commited on
Commit
f42e3d3
·
verified ·
1 Parent(s): ad2d867

Upload 4 files

Browse files
Dockerfile ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ghcr.io/home-assistant/home-assistant:stable
2
+
3
+ USER root
4
+
5
+ RUN apk add --no-cache git bash \
6
+ && git clone --depth 1 https://github.com/al-one/hass-xiaomi-miot.git /tmp/hass-xiaomi-miot \
7
+ && mkdir -p /opt/ha-custom-components \
8
+ && cp -a /tmp/hass-xiaomi-miot/custom_components/xiaomi_miot /opt/hass-custom-components/xiaomi_miot \
9
+ && rm -rf /tmp/hass-xiaomi-miot \
10
+ && python3 -m pip install --no-cache-dir --disable-pip-version-check huggingface_hub \
11
+ && mkdir -p /data/home-assistant /config
12
+
13
+ COPY scripts/start-homeassistant /usr/local/bin/start-homeassistant
14
+ COPY scripts/backup-homeassistant.py /usr/local/bin/backup-homeassistant.py
15
+ RUN chmod +x /usr/local/bin/start-homeassistant
16
+
17
+ ENV HOME_ASSISTANT_CONFIG=/data/home-assistant
18
+ ENV PORT=7860
19
+
20
+ EXPOSE 7860
21
+ ENTRYPOINT ["/usr/local/bin/start-homeassistant"]
README.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Home Assistant Mi Home
3
+ emoji: 🏠
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ app_port: 7860
8
+ suggested_hardware: cpu-basic
9
+ ---
10
+
11
+ # Home Assistant on Hugging Face Spaces
12
+
13
+ This Space runs Home Assistant with the Xiaomi Miot custom integration pre-installed.
14
+
15
+ ## Required Space secrets
16
+
17
+ The web interface works without backup secrets, but the free Space disk is ephemeral.
18
+ For configuration recovery after a Space rebuild, create a private Dataset on the
19
+ Hugging Face Hub and add these Space secrets:
20
+
21
+ - `HF_TOKEN`: a token with write access to the private Dataset
22
+ - `BACKUP_REPO_ID`: Dataset id, for example `your-name/home-assistant-backup`
23
+
24
+ The startup script restores `homeassistant-latest.tar.gz` when it exists and uploads a
25
+ new snapshot every 10 minutes. The backup includes Home Assistant's `.storage` folder,
26
+ so integrations and onboarding state are retained. It excludes the recorder database,
27
+ logs, cache, and media to keep the backup small.
28
+
29
+ ## First use
30
+
31
+ Open the Space at its public URL and complete the Home Assistant onboarding flow. In
32
+ **Settings -> Devices & services**, add **Xiaomi Miot Auto** and sign in with the Mi
33
+ Home account. This integration generally requires the Xiaomi cloud region that owns
34
+ the devices; it does not require the Home Assistant container to be on the same LAN.
35
+
36
+ Do not put `HF_TOKEN` in this repository or in `README.md`; use Space Settings ->
37
+ Repository secrets.
scripts/backup-homeassistant.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Restore and upload a compact Home Assistant configuration snapshot."""
2
+ from __future__ import annotations
3
+ import argparse
4
+ import os
5
+ import sys
6
+ import tarfile
7
+ import tempfile
8
+ from pathlib import Path
9
+
10
+ ARCHIVE_NAME = "homeassistant-latest.tar.gz"
11
+
12
+
13
+ def hub_api():
14
+ from huggingface_hub import HfApi
15
+ token = os.environ.get("HF_TOKEN")
16
+ repo_id = os.environ.get("BACKUP_REPO_ID")
17
+ if not token or not repo_id:
18
+ raise RuntimeError("HF_TOKEN and BACKUP_REPO_ID are required for Hub backups")
19
+ api = HfApi(token=token)
20
+ repo_type = os.environ.get("BACKUP_REPO_TYPE", "dataset")
21
+ try:
22
+ api.repo_info(repo_id=repo_id, repo_type=repo_type)
23
+ except Exception:
24
+ api.create_repo(repo_id=repo_id, repo_type=repo_type, private=True, exist_ok=True)
25
+ return api, repo_id, repo_type, token
26
+
27
+
28
+ def archive_config(config: Path, archive: Path) -> None:
29
+ with tarfile.open(archive, "w:gz") as tar:
30
+ for path in config.rglob("*"):
31
+ if not path.is_file() or path.is_symlink(): continue
32
+ relative = path.relative_to(config).as_posix()
33
+ if relative in {"home-assistant_v2.db", "home-assistant.log"}: continue
34
+ if relative.endswith((".db-wal", ".db-shm")): continue
35
+ if relative.startswith((".cloud/", "tts/")): continue
36
+ tar.add(path, arcname=relative, recursive=False)
37
+
38
+
39
+ def backup(config: Path) -> None:
40
+ api, repo_id, repo_type, token = hub_api()
41
+ with tempfile.TemporaryDirectory() as temp_dir:
42
+ archive = Path(temp_dir) / ARCHIVE_NAME
43
+ archive_config(config, archive)
44
+ api.upload_file(path_or_fileobj=str(archive), path_in_repo=ARCHIVE_NAME,
45
+ repo_id=repo_id, repo_type=repo_type, token=token,
46
+ commit_message="Update Home Assistant configuration backup")
47
+ print(f"Uploaded {ARCHIVE_NAME} to {repo_type}/{repo_id}", flush=True)
48
+
49
+
50
+ def restore(config: Path) -> None:
51
+ try:
52
+ _, repo_id, repo_type, token = hub_api()
53
+ from huggingface_hub import hf_hub_download
54
+ archive = hf_hub_download(repo_id=repo_id, filename=ARCHIVE_NAME,
55
+ repo_type=repo_type, token=token, force_download=True)
56
+ except Exception as exc:
57
+ print(f"No Hub backup restored: {exc}", file=sys.stderr, flush=True)
58
+ return
59
+ config.mkdir(parents=True, exist_ok=True)
60
+ with tarfile.open(archive, "r:gz") as tar:
61
+ root = config.resolve()
62
+ for member in tar.getmembers():
63
+ target = (config / member.name).resolve()
64
+ if root not in target.parents: raise RuntimeError(f"Unsafe backup path: {member.name}")
65
+ tar.extractall(config)
66
+ print(f"Restored {ARCHIVE_NAME} into {config}", flush=True)
67
+
68
+
69
+ def main() -> int:
70
+ parser = argparse.ArgumentParser()
71
+ parser.add_argument("action", choices=("backup", "restore"))
72
+ parser.add_argument("--config", required=True, type=Path)
73
+ args = parser.parse_args()
74
+ if not os.environ.get("HF_TOKEN") or not os.environ.get("BACKUP_REPO_ID"): return 0
75
+ (backup if args.action == "backup" else restore)(args.config)
76
+ return 0
77
+
78
+
79
+ if __name__ == "__main__": raise SystemExit(main())
scripts/start-homeassistant ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -Eeuo pipefail
3
+
4
+ CONFIG_DIR="${HOME_ASSISTANT_CONFIG:-/data/home-assistant}"
5
+ CUSTOM_COMPONENT="/opt/hass-custom-components/xiaomi_miot"
6
+
7
+ mkdir -p "$CONFIG_DIR/custom_components"
8
+ if [[ ! -f "$CONFIG_DIR/configuration.yaml" ]]; then
9
+ python3 /usr/local/bin/backup-homeassistant.py restore --config "$CONFIG_DIR" || true
10
+ fi
11
+
12
+ if [[ ! -d "$CONFIG_DIR/custom_components/xiaomi_miot" ]]; then
13
+ cp -a "$CUSTOM_COMPONENT" "$CONFIG_DIR/custom_components/xiaomi_miot"
14
+ fi
15
+
16
+ if [[ ! -f "$CONFIG_DIR/configuration.yaml" ]]; then
17
+ cat > "$CONFIG_DIR/configuration.yaml" <<'YAML'
18
+ default_config:
19
+
20
+ http:
21
+ server_port: 7860
22
+ YAML
23
+ fi
24
+
25
+ if [[ -n "${HF_TOKEN:-}" && -n "${BACKUP_REPO_ID:-}" ]]; then
26
+ (
27
+ while sleep 600; do
28
+ python3 /usr/local/bin/backup-homeassistant.py backup --config "$CONFIG_DIR" || true
29
+ done
30
+ ) &
31
+ BACKUP_PID=$!
32
+ else
33
+ BACKUP_PID=""
34
+ fi
35
+
36
+ cleanup() {
37
+ if [[ -n "$BACKUP_PID" ]]; then kill "$BACKUP_PID" 2>/dev/null || true; fi
38
+ if [[ -n "${HF_TOKEN:-}" && -n "${BACKUP_REPO_ID:-}" ]]; then
39
+ python3 /usr/local/bin/backup-homeassistant.py backup --config "$CONFIG_DIR" || true
40
+ fi
41
+ }
42
+ trap cleanup EXIT TERM INT
43
+
44
+ hass --config "$CONFIG_DIR" --port 7860 &
45
+ HA_PID=$!
46
+ wait "$HA_PID"