#!/home/noise/.local/share/uv/tools/powermon/bin/python """eg4-purge-orphans — remove deprecated HA-discovery entities from MQTT. After we drop fields from the daemon's published set (raw register_NN, the superseded bms_version_hi/lo u16s, the noisy uptime_ds counter, etc.), the old discovery configs remain RETAINED in the broker — so HA keeps the orphaned entities forever. This tool publishes an empty payload (with retain=true) to each orphaned config topic, which tells HA "forget this entity" and clears the broker's retained slot. Idempotent + safe to re-run. Doesn't touch live entities (those get re-published by the daemon every cycle). Usage: eg4-purge-orphans [--dry-run] """ from __future__ import annotations import sys import argparse import paho.mqtt.client as mqtt PACK_NAMES = ["lifepower4_1", "lifepower4_2", "lifepower4_3"] # Build the topic list to purge. DEPRECATED_KEYS: list[str] = [] for n in range(0, 136): DEPRECATED_KEYS.append(f"register_{n:02d}") # 136 raw register entities DEPRECATED_KEYS.append("bms_version_hi") # superseded by firmware_version DEPRECATED_KEYS.append("bms_version_lo") # superseded by firmware_version DEPRECATED_KEYS.append("uptime_ds") # noisy counter PREFIX = "homeassistant/sensor" def main() -> int: ap = argparse.ArgumentParser() ap.add_argument("host") ap.add_argument("user") ap.add_argument("password") ap.add_argument("--dry-run", action="store_true", help="print topics that would be purged, don't publish") ap.add_argument("--port", type=int, default=1883) args = ap.parse_args() topics: list[str] = [] for pack in PACK_NAMES: for key in DEPRECATED_KEYS: topics.append(f"{PREFIX}/{pack}_{key}/config") print(f"will purge {len(topics)} topic(s) " f"({len(DEPRECATED_KEYS)} keys × {len(PACK_NAMES)} packs)") if args.dry_run: for t in topics[:5]: print(f" (dry-run) {t}") print(f" ... and {len(topics) - 5} more") return 0 # Use legacy constructor — works on both paho-mqtt 1.x and 2.x try: c = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="eg4-purge") except AttributeError: c = mqtt.Client(client_id="eg4-purge") c.username_pw_set(args.user, args.password) c.connect(args.host, args.port, keepalive=30) c.loop_start() try: for i, t in enumerate(topics): info = c.publish(t, payload="", qos=0, retain=True) info.wait_for_publish(2) if (i + 1) % 50 == 0: print(f" ...{i + 1}/{len(topics)}") print(f"done — {len(topics)} retained configs cleared") finally: c.loop_stop() c.disconnect() return 0 if __name__ == "__main__": sys.exit(main())