#!/usr/bin/env python3
# clone_eeprom.py — простой клонер 1-Wire EEPROM (DS2433 style)
# Run with sudo: sudo python3 clone_eeprom.py

import time, os, glob, hashlib, subprocess, sys

W1_PATH = "/sys/bus/w1/devices"
DUMP_DIR = "dumps"
READ_TIMEOUT = 12    # seconds for read (adjust if needed)
HOLD_SECONDS = 6     # how long user should hold the button

def list_devices():
    # look for 1-wire device ids that typically start with "23-" (DS2433 family)
    ids = []
    for p in glob.glob(os.path.join(W1_PATH, "23-*")):
        ids.append(os.path.basename(p))
    return sorted(ids)

def wait_for_device(prompt):
    print(prompt)
    print(f"Нажмите и держите кнопку ~{HOLD_SECONDS}s, затем Enter чтобы начать ожидание...")
    input()
    print("Ожидаю появления устройства на шине 1-Wire...")
    # poll for device for up to 20 seconds
    for i in range(30):
        ids = list_devices()
        if ids:
            uid = ids[0]
            print("Найдено устройство:", uid)
            return uid
        time.sleep(1)
    print("Устройство не появилось. Проверьте проводку/кнопку и попробуйте снова.")
    return None

def read_eeprom(uid, outpath):
    eeprom_path = os.path.join(W1_PATH, uid, "eeprom")
    if not os.path.exists(eeprom_path):
        # sometimes device layout includes w1_bus_master1 folder variant
        alt = glob.glob(os.path.join(W1_PATH, "w1_bus_master1", uid, "eeprom"))
        if alt:
            eeprom_path = alt[0]
    if not os.path.exists(eeprom_path):
        raise FileNotFoundError("eeprom path not found for " + uid)
    print("Читаю EEPROM из", eeprom_path)
    # use dd via shell with timeout to avoid hangs
    cmd = f"timeout {READ_TIMEOUT} dd if={eeprom_path} bs=1 count=512 of={outpath}"
    rc = subprocess.call(cmd, shell=True)
    if rc != 0:
        raise RuntimeError("Чтение EEPROM не удалось (dd rc="+str(rc)+")")
    size = os.path.getsize(outpath)
    if size != 512:
        raise RuntimeError(f"Неполный дамп: {size} байт (ожидалось 512)")
    print("Записал дамп в", outpath)
    return outpath

def write_eeprom(uid, inpath):
    eeprom_path = os.path.join(W1_PATH, uid, "eeprom")
    if not os.path.exists(eeprom_path):
        alt = glob.glob(os.path.join(W1_PATH, "w1_bus_master1", uid, "eeprom"))
        if alt:
            eeprom_path = alt[0]
    if not os.path.exists(eeprom_path):
        raise FileNotFoundError("eeprom path not found for " + uid)
    print("Записываю", inpath, "->", eeprom_path)
    # use cat with sudo via shell; assume script runs as root, so direct redirect works
    cmd = f"dd if={inpath} bs=1 count=512 of={eeprom_path}"
    rc = subprocess.call(cmd, shell=True)
    if rc != 0:
        raise RuntimeError("Запись EEPROM не удалась (rc="+str(rc)+")")
    print("Запись завершена.")
    return eeprom_path

def sha1(path):
    h = hashlib.sha1()
    with open(path,"rb") as f:
        h.update(f.read())
    return h.hexdigest()

def ensure_dir(d):
    if not os.path.exists(d):
        os.makedirs(d, exist_ok=True)

def main():
    if os.geteuid() != 0:
        print("Запускать нужно с sudo: sudo python3 clone_eeprom.py")
        sys.exit(1)
    ensure_dir(DUMP_DIR)

    # First chip
    uid1 = wait_for_device("Подключите ПЕРВЫЙ чип.")
    if not uid1:
        return
    time.sleep(0.5)
    out1 = os.path.join(DUMP_DIR, f"{uid1}.bin")
    try:
        print(f"Держите кнопку ещё {HOLD_SECONDS}s...")
        time.sleep(HOLD_SECONDS)
        read_eeprom(uid1, out1)
    except Exception as e:
        print("Ошибка при чтении первого чипа:", e)
        return
    print("SHA1:", sha1(out1))

    # Second chip
    print("\nОтключите ПЕРВЫЙ чип и ПОДКЛЮЧИТЕ ВТОРОЙ чип.")
    input("Когда второй чип подключён — нажмите Enter...")
    uid2 = wait_for_device("Подключите ВТОРОЙ чип.")
    if not uid2:
        return
    if uid2 == uid1:
        print("Обнаружен тот же UID! Убедитесь, что второй чип подключён.")
        # still allow: continue
    out2 = os.path.join(DUMP_DIR, f"{uid2}.bin")
    try:
        print(f"Держите кнопку ещё {HOLD_SECONDS}s...")
        time.sleep(HOLD_SECONDS)
        # before writing, read existing content (backup)
        try:
            read_eeprom(uid2, out2+".backup")
            print("Сделан бэкап второго чипа:", out2+".backup")
        except Exception as be:
            print("Не удалось снять бэкап второго (может быть пустой), продолжаем:", be)
        # write first dump to second
        write_eeprom(uid2, out1)
        # read back and compare
        read_eeprom(uid2, out2)
        print("SHA1 source:", sha1(out1))
        print("SHA1 target:", sha1(out2))
        if sha1(out1) == sha1(out2):
            print("\nКлонирование успешно! (хеши совпадают)")
        else:
            print("\nВнимание: хеши не совпадают. Проверьте соединения и повтрите процедуру.")
    except Exception as e:
        print("Ошибка при записи/проверке:", e)

if __name__ == "__main__":
    main()
