84 lines
2.8 KiB
Plaintext
84 lines
2.8 KiB
Plaintext
|
|
#!/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 <broker> <user> <password> [--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())
|