#!/usr/bin/env python3
"""
Synchronizuje friendly_names w ibsystem2mqtt.yaml z dokumentacji projektu.
Źródło: /smart-home/proj-ib-lighting-rs485-zarki.yaml
"""
import yaml, re, sys, os, subprocess, shutil
from datetime import datetime
PROJECT_YAML = "/smart-home/proj-ib-lighting-rs485-zarki.yaml"
CONFIG_YAML = "/ibsystem/ibsystem2mqtt.yaml"
MQTT_HOST = "192.168.50.151"
MQTT_USER = "mqtt"
MQTT_PASS = "mqtt123"
def device_id_to_rs_id(device_id):
"""'rs.0.id.35' → ('0', '35')"""
m = re.match(r"rs\.(\d+)\.id\.(\d+)", device_id)
return (m.group(1), m.group(2)) if m else (None, None)
def io_key(rs, dev_id, io_id):
"""'output.do.0' → 'rs0_id35_output_do_0'"""
safe = re.sub(r"[^a-zA-Z0-9]+", "_", io_id).strip("_")
return f"rs{rs}_id{dev_id}_{safe}"
def parse_project_yaml(path):
with open(path) as f:
data = yaml.safe_load(f)
devices = data["project"]["hardware_specification"]["devices"]
names = {}
for dev in devices:
device_id = dev.get("device_id", "")
location = dev.get("location", "").strip()
rs, dev_id = device_id_to_rs_id(device_id)
if not rs:
continue
# Nazwa urządzenia = lokalizacja + ID
if location:
names[f"rs{rs}_id{dev_id}"] = f"{location} - ID{dev_id}"
# Nazwy wyjść DO (sterowalne)
for io in dev.get("io", []):
io_id = io.get("io_id", "")
desc = io.get("description", "").strip()
status = io.get("status", "ok")
# Tylko output.do.* z niepustym opisem i status != nc
if io_id.startswith("output.do.") and desc and status != "nc":
# Pomiń generyczne opisy
if not desc.startswith("Opcjonalne"):
names[io_key(rs, dev_id, io_id)] = desc
return names
def update_config(config_path, new_names, dry_run=False):
with open(config_path) as f:
config = yaml.safe_load(f)
old_names = config.get("friendly_names") or {}
# Oblicz diff
changed = {}
all_keys = set(old_names) | set(new_names)
for k in sorted(all_keys):
o, n = old_names.get(k), new_names.get(k)
if o != n:
changed[k] = (o, n)
if not changed:
print(f"[{datetime.now():%H:%M:%S}] Brak zmian.")
return False
print(f"[{datetime.now():%H:%M:%S}] Zmiany ({len(changed)}):")
for k, (o, n) in changed.items():
print(f" {k}: {o!r} → {n!r}")
if dry_run:
print(" (dry-run, nie zapisuję)")
return True
# Zapisz backup
shutil.copy(config_path, config_path + ".bak")
config["friendly_names"] = new_names
with open(config_path, "w") as f:
yaml.dump(config, f, allow_unicode=True, default_flow_style=False, sort_keys=False)
print(f" Zapisano {config_path}")
return True
def reload_ibsystem():
"""Wyczyść retained MQTT discovery i zrestartuj serwis."""
print("Czyszczę MQTT discovery cache...")
os.system(
f"timeout 5 mosquitto_sub -h {MQTT_HOST} -u {MQTT_USER} -P {MQTT_PASS} "
f"-t 'homeassistant/#' --retained-only -v 2>/dev/null | "
f"grep '/config' | grep 'ibsystem' | awk '{{print $1}}' | "
f"while read t; do mosquitto_pub -h {MQTT_HOST} -u {MQTT_USER} -P {MQTT_PASS} "
f"-t \"$t\" -n -r; done"
)
print("Restartuję ibsystem2mqtt...")
subprocess.run(["systemctl", "restart", "ibsystem2mqtt"], check=True)
print("Gotowe.")
def main():
dry_run = "--dry-run" in sys.argv
print(f"[{datetime.now():%H:%M:%S}] Parsing {PROJECT_YAML}...")
try:
names = parse_project_yaml(PROJECT_YAML)
except Exception as e:
print(f"BŁĄD parsowania YAML: {e}")
sys.exit(1)
print(f" Znaleziono {len(names)} wpisów.")
changed = update_config(CONFIG_YAML, names, dry_run)
if changed and not dry_run:
reload_ibsystem()
if __name__ == "__main__":
main()