diff --git a/ibsystem/config.yaml b/ibsystem/config.yaml index 41e2e9f..d4c9dd9 100644 --- a/ibsystem/config.yaml +++ b/ibsystem/config.yaml @@ -1,42 +1,113 @@ -# IBSystem2MQTT - Konfiguracja mqtt: brokers: - - name: ha - host: 192.168.50.151 - port: 1883 + - name: ha + host: 192.168.50.151 + port: 1883 user: mqtt password: mqtt123 prefix: ibsystem ha_prefix: homeassistant - ibsystem: host: 127.0.0.1 port: 2001 rs: 0 max_ids: 40 - full_ids: [1] + full_ids: + - 1 timeout_ms: 10000 - polling: interval_sec: 2.0 parallel_workers: 8 - logging: level: INFO file: /var/log/ibsystem2mqtt.log - http: enabled: true host: 0.0.0.0 port: 8080 - -# Friendly names - mapowanie ID na nazwy friendly_names: - # rs0_id1: "Centrala" - # rs0_id2: "Salon" - # rs0_id2_output_do_0: "Salon Glowne Swiatlo" - rs0_id35: "Biuro/Garderoba - ID35" - rs0_id35_output_do_0: Oswietlenie BW1 - rs0_id35_output_do_1: Oswietlenie BW2 - rs0_id35_output_do_2: Oswietlenie G1 - rs0_id35_output_do_3: Oswietlenie G2 led nad szafami + rs0_id2: ID2 Sterownik ogrzewania podłogowego - ID2 + rs0_id2_output_do_0: zawór mieszający Rozdzielacz R1-p.tech (zimnej, dobór z powrotu) + rs0_id2_output_do_1: zawór mieszający Rozdzielacz R1-p.tech (ciepłej, dobór z zasilania[bufor] + rs0_id2_output_do_2: zawór mieszający Rozdzielacz R2-salon (zimnej, dobór z powrotu) + rs0_id2_output_do_3: zawór mieszający Rozdzielacz R2-salon (ciepłej, dobór z zasilania[bufor] + rs0_id3: ID3 - ID3 + rs0_id3_output_do_0: siłownik rozdzielacz R1 - Basia + rs0_id3_output_do_1: siłownik rozdzielacz R1 - Ania + rs0_id3_output_do_2: siłownik pokoju - WC + rs0_id3_output_do_3: siłownik pokoju - Wiatrołap + rs0_id4: ID4 - ID4 + rs0_id4_output_do_0: siłownik rozdzielacz R2 - salon + rs0_id4_output_do_1: siłownik rozdzielacz R2 - biuro Wojtek + rs0_id4_output_do_2: siłownik rozdzielacz R2 - sypialnia + rs0_id4_output_do_3: siłownik rozdzielacz R2 - duża łazienka + rs0_id5: ID10 Sterownik kominka - ID5 + rs0_id5_output_do_0: pompa obiegowa kominka + rs0_id5_output_do_1: elektro zawór zraszaczy sekcja X + rs0_id5_output_do_2: elektro zawór zraszaczy sekcja X + rs0_id5_output_do_3: elektro zawór zraszaczy sekcja X + rs0_id6: ID6 - ID6 + rs0_id6_output_do_0: pompa głębinowa + rs0_id6_output_do_1: elektro zawór zraszaczy sekcja X + rs0_id6_output_do_2: elektro zawór zraszaczy sekcja X + rs0_id6_output_do_3: elektro zawór zraszaczy sekcja X + rs0_id30: Wiatrołap - ID30 + rs0_id30_output_do_0: Oświetlenie H1 (Led bar AP) + rs0_id30_output_do_1: Oświetlenie H2 led + rs0_id30_output_do_2: Oświetlenie K1 szynoprzewód nowodworski + rs0_id30_output_do_3: Oświetlenie K2 szynoprzewód nowodworski + rs0_id31: Kuchnia - ID31 + rs0_id31_output_do_0: Oświetlenie K3 halogeny okno + rs0_id31_output_do_1: Oświetlenie K4 lampy nad barem + rs0_id31_output_do_2: Oświetlenie K5 półki led + rs0_id31_output_do_3: Oświetlenie K6 led okno + rs0_id32: Jadalnia / Salon - ID32 + rs0_id32_output_do_0: Oświetlenie J1 + rs0_id32_output_do_1: Oświetlenie J2 + rs0_id32_output_do_2: Oświetlenie S1 Ring + rs0_id32_output_do_3: Oświetlenie S2 lampy za kanapą + rs0_id33: Salon - ID33 + rs0_id33_output_do_0: Oświetlenie lampa przed tv + rs0_id33_output_do_1: Oświetlenie led + rs0_id33_output_do_2: Oświetlenie dekoracyjne koło lampy + rs0_id33_output_do_3: Oświetlenie led sufit wnęka + rs0_id34: Korytarz - ID34 + rs0_id34_output_do_0: Oświetlenie H3 szynoprzewód nowodworski + rs0_id34_output_do_1: Oświetlenie H4 szynoprzewód nowodworski + rs0_id34_output_do_2: Oświetlenie H5 ledy pionowe + rs0_id34_output_do_3: Oświetlenie H6 led kominek + rs0_id35: Biuro W/ Garderoba - ID35 + rs0_id35_output_do_0: Oświetlenie BW1 + rs0_id35_output_do_1: Oświetlenie BW2 + rs0_id35_output_do_2: Oświetlenie G1 + rs0_id35_output_do_3: Oświetlenie G2 led nad szafami i koło lustra + rs0_id36: Sypialnia - ID36 + rs0_id36_output_do_0: Oświetlenie R1 główne + rs0_id36_output_do_1: Oświetlenie R2 nakastlik P + rs0_id36_output_do_2: Oświetlenie R3 nakastlik L + rs0_id36_output_do_3: Oświetlenie R4 led na suficie + rs0_id37: Łazienka - ID37 + rs0_id37_output_do_0: Oświetlenie L1 + rs0_id37_output_do_1: Oświetlenie L2 + rs0_id37_output_do_2: Oświetlenie L3 + rs0_id37_output_do_3: Oświetlenie L4 + rs0_id38: Łazienka/WC - ID38 + rs0_id38_output_do_0: Oświetlenie L5 + rs0_id38_output_do_1: Oświetlenie L6 + rs0_id38_output_do_2: Oświetlenie W1 + rs0_id38_output_do_3: Oświetlenie W2 + rs0_id39: Basia - ID39 + rs0_id39_output_do_0: Oświetlenie D1 główne + rs0_id39_output_do_1: Oświetlenie D2 nad biurkiem + rs0_id39_output_do_2: Oświetlenie D3 chmurka + rs0_id39_output_do_3: Oświetlenie D4 łóżko + rs0_id40: Biuro A - ID40 + rs0_id40_output_do_0: Oświetlenie B1 główne + rs0_id40_output_do_1: Oświetlenie B2 nad biurkiem + rs0_id40_output_do_2: Oświetlenie B3 biurko lampa + rs0_id40_output_do_3: Oświetlenie B4 dla ledów + rs0_id41: WC/Poddasze - ID41 + rs0_id41_output_do_0: Oświetlenie W2 góra halogeny + rs0_id41_output_do_1: Oświetlenie W3 halogen prysznic + led + rs0_id41_output_do_2: Oświetlenie P1 Poddasze 1 + rs0_id41_output_do_3: Oświetlenie P2 Poddasze 2 diff --git a/ibsystem/config.yaml b/ibsystem/config.yaml index 41e2e9f..d4c9dd9 100644 --- a/ibsystem/config.yaml +++ b/ibsystem/config.yaml @@ -1,42 +1,113 @@ -# IBSystem2MQTT - Konfiguracja mqtt: brokers: - - name: ha - host: 192.168.50.151 - port: 1883 + - name: ha + host: 192.168.50.151 + port: 1883 user: mqtt password: mqtt123 prefix: ibsystem ha_prefix: homeassistant - ibsystem: host: 127.0.0.1 port: 2001 rs: 0 max_ids: 40 - full_ids: [1] + full_ids: + - 1 timeout_ms: 10000 - polling: interval_sec: 2.0 parallel_workers: 8 - logging: level: INFO file: /var/log/ibsystem2mqtt.log - http: enabled: true host: 0.0.0.0 port: 8080 - -# Friendly names - mapowanie ID na nazwy friendly_names: - # rs0_id1: "Centrala" - # rs0_id2: "Salon" - # rs0_id2_output_do_0: "Salon Glowne Swiatlo" - rs0_id35: "Biuro/Garderoba - ID35" - rs0_id35_output_do_0: Oswietlenie BW1 - rs0_id35_output_do_1: Oswietlenie BW2 - rs0_id35_output_do_2: Oswietlenie G1 - rs0_id35_output_do_3: Oswietlenie G2 led nad szafami + rs0_id2: ID2 Sterownik ogrzewania podłogowego - ID2 + rs0_id2_output_do_0: zawór mieszający Rozdzielacz R1-p.tech (zimnej, dobór z powrotu) + rs0_id2_output_do_1: zawór mieszający Rozdzielacz R1-p.tech (ciepłej, dobór z zasilania[bufor] + rs0_id2_output_do_2: zawór mieszający Rozdzielacz R2-salon (zimnej, dobór z powrotu) + rs0_id2_output_do_3: zawór mieszający Rozdzielacz R2-salon (ciepłej, dobór z zasilania[bufor] + rs0_id3: ID3 - ID3 + rs0_id3_output_do_0: siłownik rozdzielacz R1 - Basia + rs0_id3_output_do_1: siłownik rozdzielacz R1 - Ania + rs0_id3_output_do_2: siłownik pokoju - WC + rs0_id3_output_do_3: siłownik pokoju - Wiatrołap + rs0_id4: ID4 - ID4 + rs0_id4_output_do_0: siłownik rozdzielacz R2 - salon + rs0_id4_output_do_1: siłownik rozdzielacz R2 - biuro Wojtek + rs0_id4_output_do_2: siłownik rozdzielacz R2 - sypialnia + rs0_id4_output_do_3: siłownik rozdzielacz R2 - duża łazienka + rs0_id5: ID10 Sterownik kominka - ID5 + rs0_id5_output_do_0: pompa obiegowa kominka + rs0_id5_output_do_1: elektro zawór zraszaczy sekcja X + rs0_id5_output_do_2: elektro zawór zraszaczy sekcja X + rs0_id5_output_do_3: elektro zawór zraszaczy sekcja X + rs0_id6: ID6 - ID6 + rs0_id6_output_do_0: pompa głębinowa + rs0_id6_output_do_1: elektro zawór zraszaczy sekcja X + rs0_id6_output_do_2: elektro zawór zraszaczy sekcja X + rs0_id6_output_do_3: elektro zawór zraszaczy sekcja X + rs0_id30: Wiatrołap - ID30 + rs0_id30_output_do_0: Oświetlenie H1 (Led bar AP) + rs0_id30_output_do_1: Oświetlenie H2 led + rs0_id30_output_do_2: Oświetlenie K1 szynoprzewód nowodworski + rs0_id30_output_do_3: Oświetlenie K2 szynoprzewód nowodworski + rs0_id31: Kuchnia - ID31 + rs0_id31_output_do_0: Oświetlenie K3 halogeny okno + rs0_id31_output_do_1: Oświetlenie K4 lampy nad barem + rs0_id31_output_do_2: Oświetlenie K5 półki led + rs0_id31_output_do_3: Oświetlenie K6 led okno + rs0_id32: Jadalnia / Salon - ID32 + rs0_id32_output_do_0: Oświetlenie J1 + rs0_id32_output_do_1: Oświetlenie J2 + rs0_id32_output_do_2: Oświetlenie S1 Ring + rs0_id32_output_do_3: Oświetlenie S2 lampy za kanapą + rs0_id33: Salon - ID33 + rs0_id33_output_do_0: Oświetlenie lampa przed tv + rs0_id33_output_do_1: Oświetlenie led + rs0_id33_output_do_2: Oświetlenie dekoracyjne koło lampy + rs0_id33_output_do_3: Oświetlenie led sufit wnęka + rs0_id34: Korytarz - ID34 + rs0_id34_output_do_0: Oświetlenie H3 szynoprzewód nowodworski + rs0_id34_output_do_1: Oświetlenie H4 szynoprzewód nowodworski + rs0_id34_output_do_2: Oświetlenie H5 ledy pionowe + rs0_id34_output_do_3: Oświetlenie H6 led kominek + rs0_id35: Biuro W/ Garderoba - ID35 + rs0_id35_output_do_0: Oświetlenie BW1 + rs0_id35_output_do_1: Oświetlenie BW2 + rs0_id35_output_do_2: Oświetlenie G1 + rs0_id35_output_do_3: Oświetlenie G2 led nad szafami i koło lustra + rs0_id36: Sypialnia - ID36 + rs0_id36_output_do_0: Oświetlenie R1 główne + rs0_id36_output_do_1: Oświetlenie R2 nakastlik P + rs0_id36_output_do_2: Oświetlenie R3 nakastlik L + rs0_id36_output_do_3: Oświetlenie R4 led na suficie + rs0_id37: Łazienka - ID37 + rs0_id37_output_do_0: Oświetlenie L1 + rs0_id37_output_do_1: Oświetlenie L2 + rs0_id37_output_do_2: Oświetlenie L3 + rs0_id37_output_do_3: Oświetlenie L4 + rs0_id38: Łazienka/WC - ID38 + rs0_id38_output_do_0: Oświetlenie L5 + rs0_id38_output_do_1: Oświetlenie L6 + rs0_id38_output_do_2: Oświetlenie W1 + rs0_id38_output_do_3: Oświetlenie W2 + rs0_id39: Basia - ID39 + rs0_id39_output_do_0: Oświetlenie D1 główne + rs0_id39_output_do_1: Oświetlenie D2 nad biurkiem + rs0_id39_output_do_2: Oświetlenie D3 chmurka + rs0_id39_output_do_3: Oświetlenie D4 łóżko + rs0_id40: Biuro A - ID40 + rs0_id40_output_do_0: Oświetlenie B1 główne + rs0_id40_output_do_1: Oświetlenie B2 nad biurkiem + rs0_id40_output_do_2: Oświetlenie B3 biurko lampa + rs0_id40_output_do_3: Oświetlenie B4 dla ledów + rs0_id41: WC/Poddasze - ID41 + rs0_id41_output_do_0: Oświetlenie W2 góra halogeny + rs0_id41_output_do_1: Oświetlenie W3 halogen prysznic + led + rs0_id41_output_do_2: Oświetlenie P1 Poddasze 1 + rs0_id41_output_do_3: Oświetlenie P2 Poddasze 2 diff --git a/ibsystem/sync_names.py b/ibsystem/sync_names.py new file mode 100755 index 0000000..b759794 --- /dev/null +++ b/ibsystem/sync_names.py @@ -0,0 +1,123 @@ +#!/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() diff --git a/ibsystem/config.yaml b/ibsystem/config.yaml index 41e2e9f..d4c9dd9 100644 --- a/ibsystem/config.yaml +++ b/ibsystem/config.yaml @@ -1,42 +1,113 @@ -# IBSystem2MQTT - Konfiguracja mqtt: brokers: - - name: ha - host: 192.168.50.151 - port: 1883 + - name: ha + host: 192.168.50.151 + port: 1883 user: mqtt password: mqtt123 prefix: ibsystem ha_prefix: homeassistant - ibsystem: host: 127.0.0.1 port: 2001 rs: 0 max_ids: 40 - full_ids: [1] + full_ids: + - 1 timeout_ms: 10000 - polling: interval_sec: 2.0 parallel_workers: 8 - logging: level: INFO file: /var/log/ibsystem2mqtt.log - http: enabled: true host: 0.0.0.0 port: 8080 - -# Friendly names - mapowanie ID na nazwy friendly_names: - # rs0_id1: "Centrala" - # rs0_id2: "Salon" - # rs0_id2_output_do_0: "Salon Glowne Swiatlo" - rs0_id35: "Biuro/Garderoba - ID35" - rs0_id35_output_do_0: Oswietlenie BW1 - rs0_id35_output_do_1: Oswietlenie BW2 - rs0_id35_output_do_2: Oswietlenie G1 - rs0_id35_output_do_3: Oswietlenie G2 led nad szafami + rs0_id2: ID2 Sterownik ogrzewania podłogowego - ID2 + rs0_id2_output_do_0: zawór mieszający Rozdzielacz R1-p.tech (zimnej, dobór z powrotu) + rs0_id2_output_do_1: zawór mieszający Rozdzielacz R1-p.tech (ciepłej, dobór z zasilania[bufor] + rs0_id2_output_do_2: zawór mieszający Rozdzielacz R2-salon (zimnej, dobór z powrotu) + rs0_id2_output_do_3: zawór mieszający Rozdzielacz R2-salon (ciepłej, dobór z zasilania[bufor] + rs0_id3: ID3 - ID3 + rs0_id3_output_do_0: siłownik rozdzielacz R1 - Basia + rs0_id3_output_do_1: siłownik rozdzielacz R1 - Ania + rs0_id3_output_do_2: siłownik pokoju - WC + rs0_id3_output_do_3: siłownik pokoju - Wiatrołap + rs0_id4: ID4 - ID4 + rs0_id4_output_do_0: siłownik rozdzielacz R2 - salon + rs0_id4_output_do_1: siłownik rozdzielacz R2 - biuro Wojtek + rs0_id4_output_do_2: siłownik rozdzielacz R2 - sypialnia + rs0_id4_output_do_3: siłownik rozdzielacz R2 - duża łazienka + rs0_id5: ID10 Sterownik kominka - ID5 + rs0_id5_output_do_0: pompa obiegowa kominka + rs0_id5_output_do_1: elektro zawór zraszaczy sekcja X + rs0_id5_output_do_2: elektro zawór zraszaczy sekcja X + rs0_id5_output_do_3: elektro zawór zraszaczy sekcja X + rs0_id6: ID6 - ID6 + rs0_id6_output_do_0: pompa głębinowa + rs0_id6_output_do_1: elektro zawór zraszaczy sekcja X + rs0_id6_output_do_2: elektro zawór zraszaczy sekcja X + rs0_id6_output_do_3: elektro zawór zraszaczy sekcja X + rs0_id30: Wiatrołap - ID30 + rs0_id30_output_do_0: Oświetlenie H1 (Led bar AP) + rs0_id30_output_do_1: Oświetlenie H2 led + rs0_id30_output_do_2: Oświetlenie K1 szynoprzewód nowodworski + rs0_id30_output_do_3: Oświetlenie K2 szynoprzewód nowodworski + rs0_id31: Kuchnia - ID31 + rs0_id31_output_do_0: Oświetlenie K3 halogeny okno + rs0_id31_output_do_1: Oświetlenie K4 lampy nad barem + rs0_id31_output_do_2: Oświetlenie K5 półki led + rs0_id31_output_do_3: Oświetlenie K6 led okno + rs0_id32: Jadalnia / Salon - ID32 + rs0_id32_output_do_0: Oświetlenie J1 + rs0_id32_output_do_1: Oświetlenie J2 + rs0_id32_output_do_2: Oświetlenie S1 Ring + rs0_id32_output_do_3: Oświetlenie S2 lampy za kanapą + rs0_id33: Salon - ID33 + rs0_id33_output_do_0: Oświetlenie lampa przed tv + rs0_id33_output_do_1: Oświetlenie led + rs0_id33_output_do_2: Oświetlenie dekoracyjne koło lampy + rs0_id33_output_do_3: Oświetlenie led sufit wnęka + rs0_id34: Korytarz - ID34 + rs0_id34_output_do_0: Oświetlenie H3 szynoprzewód nowodworski + rs0_id34_output_do_1: Oświetlenie H4 szynoprzewód nowodworski + rs0_id34_output_do_2: Oświetlenie H5 ledy pionowe + rs0_id34_output_do_3: Oświetlenie H6 led kominek + rs0_id35: Biuro W/ Garderoba - ID35 + rs0_id35_output_do_0: Oświetlenie BW1 + rs0_id35_output_do_1: Oświetlenie BW2 + rs0_id35_output_do_2: Oświetlenie G1 + rs0_id35_output_do_3: Oświetlenie G2 led nad szafami i koło lustra + rs0_id36: Sypialnia - ID36 + rs0_id36_output_do_0: Oświetlenie R1 główne + rs0_id36_output_do_1: Oświetlenie R2 nakastlik P + rs0_id36_output_do_2: Oświetlenie R3 nakastlik L + rs0_id36_output_do_3: Oświetlenie R4 led na suficie + rs0_id37: Łazienka - ID37 + rs0_id37_output_do_0: Oświetlenie L1 + rs0_id37_output_do_1: Oświetlenie L2 + rs0_id37_output_do_2: Oświetlenie L3 + rs0_id37_output_do_3: Oświetlenie L4 + rs0_id38: Łazienka/WC - ID38 + rs0_id38_output_do_0: Oświetlenie L5 + rs0_id38_output_do_1: Oświetlenie L6 + rs0_id38_output_do_2: Oświetlenie W1 + rs0_id38_output_do_3: Oświetlenie W2 + rs0_id39: Basia - ID39 + rs0_id39_output_do_0: Oświetlenie D1 główne + rs0_id39_output_do_1: Oświetlenie D2 nad biurkiem + rs0_id39_output_do_2: Oświetlenie D3 chmurka + rs0_id39_output_do_3: Oświetlenie D4 łóżko + rs0_id40: Biuro A - ID40 + rs0_id40_output_do_0: Oświetlenie B1 główne + rs0_id40_output_do_1: Oświetlenie B2 nad biurkiem + rs0_id40_output_do_2: Oświetlenie B3 biurko lampa + rs0_id40_output_do_3: Oświetlenie B4 dla ledów + rs0_id41: WC/Poddasze - ID41 + rs0_id41_output_do_0: Oświetlenie W2 góra halogeny + rs0_id41_output_do_1: Oświetlenie W3 halogen prysznic + led + rs0_id41_output_do_2: Oświetlenie P1 Poddasze 1 + rs0_id41_output_do_3: Oświetlenie P2 Poddasze 2 diff --git a/ibsystem/sync_names.py b/ibsystem/sync_names.py new file mode 100755 index 0000000..b759794 --- /dev/null +++ b/ibsystem/sync_names.py @@ -0,0 +1,123 @@ +#!/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() diff --git a/ibsystem/systemd/ibsystem-sync-names.service b/ibsystem/systemd/ibsystem-sync-names.service new file mode 100644 index 0000000..3be8d62 --- /dev/null +++ b/ibsystem/systemd/ibsystem-sync-names.service @@ -0,0 +1,9 @@ +[Unit] +Description=Synchronize IBSystem friendly names from project documentation +After=network.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/python3 /ibsystem/sync_names.py +StandardOutput=journal +StandardError=journal