diff --git a/spawn_esphome.py b/spawn_esphome.py new file mode 100644 index 0000000..7fcbfac --- /dev/null +++ b/spawn_esphome.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +import argparse +import subprocess +import json +import re +import sys +from pathlib import Path +import requests + +DOCKER_NAMESPACE = "esphome" +DOCKER_REPO = "esphome" +DOCKER_API = f"https://registry.hub.docker.com/v2/repositories/{DOCKER_NAMESPACE}/{DOCKER_REPO}/tags" + +def parse_version(raw_version): + if len(raw_version) == 8 and raw_version.isdigit(): + yyyy = raw_version[:4] + m = str(int(raw_version[4:6])) + d = str(int(raw_version[6:])) + return f"{yyyy}.{m}.{d}" + return raw_version + +def list_tags(upto_year): + tags = [] + pattern = re.compile(r"^(\d{4})\.(\d{1,2})\.(\d{1,2})$") # dokładnie 3 liczby rozdzielone kropkami + page = 1 + while True: + resp = requests.get(DOCKER_API, params={"page_size": 100, "page": page}) + resp.raise_for_status() + data = resp.json() + for entry in data.get("results", []): + name = entry["name"] + match = pattern.match(name) + if match: + year = int(match.group(1)) + if year <= upto_year: + tags.append(name) + if not data.get("next"): + break + page += 1 + tags.sort(key=lambda s: list(map(int, s.split('.')))) + return tags +def pull_image(tag): + full = f"{DOCKER_NAMESPACE}/{DOCKER_REPO}:{tag}" + subprocess.run(["docker", "pull", full], check=True) + return full + +def run_container(version_tag, port): + container = f"esphome-{version_tag.replace('/', '_')}" + data_dir = f"/docker/data/esphome/{version_tag}" + image = f"{DOCKER_NAMESPACE}/{DOCKER_REPO}:{version_tag}" + Path(data_dir).mkdir(parents=True, exist_ok=True) + + cmd = [ + "docker", "run", "-d", + "--name", container, + "--restart", "always", + "-p", f"{port}:6052", + "-v", f"{data_dir}:/config", + image, + "dashboard", "/config" + ] + print("Uruchamianie:", " ".join(cmd)) + subprocess.run(cmd, check=True) + print("Kontener uruchomiony.") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--version", help="yyyymmdd lub yyyy.m.d") + parser.add_argument("--port", type=int, help="Port host →6052") + parser.add_argument("--list-available", type=int, metavar="YEAR", + help="Wyświetl tagi do danego roku (np. 2025)") + args = parser.parse_args() + + if args.list_available is not None: + tags = list_tags(args.list_available) + print(f"Dostępne tagi do roku {args.list_available}:") + for t in tags: + print(" ", t) + sys.exit(0) + + if not args.version or not args.port: + parser.error("Wymagane: --version i --port albo sam --list-available") + + raw = args.version + tag = parse_version(raw) + + # Sprawdź lokalnie + exists = subprocess.run(["docker", "images", "-q", f"{DOCKER_NAMESPACE}/{DOCKER_REPO}:{tag}"], + stdout=subprocess.DEVNULL).returncode == 0 + if not exists: + print(f"Obraz {tag} nie znaleziony lokalnie. Pobieram z Docker Hub...") + try: + pull_image(tag) + except subprocess.CalledProcessError: + sys.exit(f"Nie udało się pobrać esphome/esphome:{tag}") + + try: + run_container(tag, args.port) + except subprocess.CalledProcessError as e: + sys.exit(f"Błąd uruchamiania kontenera: {e}") + +if __name__ == "__main__": + main() \ No newline at end of file