#!/usr/bin/env python3 """Deploy AC automations from automations/*.yaml to Home Assistant. Source of truth is THIS repo. Home Assistant (remote, API-only) is the deploy target. Each YAML file's ``id`` is the key: the script POSTs to ``/api/config/automation/config/`` which replaces that automation *in place* (same id -> same entity_id, run history and traces stay continuous). Usage: source ~/.bashrc # provides HA_API_KEY python3 deploy.py --check # parse/validate only, no writes python3 deploy.py # deploy every automations/*.yaml python3 deploy.py ac_peak_ends # deploy only the file(s) whose id matches Env: HA_API_KEY long-lived HA token (required to deploy) HA_HOST host:port (default 192.168.128.3:8123) """ import glob import json import os import sys import urllib.error import urllib.request import yaml HERE = os.path.dirname(os.path.abspath(__file__)) HA_HOST = os.environ.get("HA_HOST", "192.168.128.3:8123") TOKEN = os.environ.get("HA_API_KEY") def load_all(): autos = [] for path in sorted(glob.glob(os.path.join(HERE, "automations", "*.yaml"))): with open(path) as fh: doc = yaml.safe_load(fh) if not doc or "id" not in doc: sys.exit(f"{path}: missing required 'id'") autos.append((path, doc)) return autos def deploy(doc): url = f"http://{HA_HOST}/api/config/automation/config/{doc['id']}" req = urllib.request.Request( url, data=json.dumps(doc).encode(), method="POST", headers={"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}, ) try: with urllib.request.urlopen(req) as r: return r.status, r.read().decode().strip() except urllib.error.HTTPError as e: return e.code, e.read().decode().strip() def main(): args = [a for a in sys.argv[1:] if not a.startswith("-")] check = "--check" in sys.argv autos = load_all() if args: autos = [(p, d) for p, d in autos if d["id"] in args] if not autos: sys.exit(f"no automations matched {args}") if not check and not TOKEN: sys.exit("HA_API_KEY not set (try: source ~/.bashrc)") rc = 0 for path, doc in autos: name = os.path.basename(path) if check: print(f"ok parse {name} id={doc['id']} alias={doc.get('alias')!r}") continue status, resp = deploy(doc) ok = status == 200 rc |= 0 if ok else 1 print(f"{'OK ' if ok else 'ERR'} {status} {name} id={doc['id']} {resp}") sys.exit(rc) if __name__ == "__main__": main()