v1.8.17: Atomarer Config-Write + Backup-Fallback
Verhindert Datenverlust wenn HAOS den Container während eines Saves stoppt. Schreibt erst config.json.tmp, dann atomares os.replace(). Hält config.json.bak als Fallback für den nächsten Start. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
name: ShineBridge
|
||||
version: "1.8.16"
|
||||
version: "1.8.17"
|
||||
slug: shinebridge
|
||||
description: Growatt Wechselrichter lokal in Home Assistant — Modbus TCP via ShineLAN-X, MQTT Discovery, Web UI
|
||||
url: https://gitea.bitfire.work/retr0/shinebridge
|
||||
|
||||
+30
-12
@@ -110,25 +110,34 @@ def _defaults() -> Dict[str, Any]:
|
||||
"z2m_base": "zigbee2mqtt",
|
||||
}
|
||||
|
||||
def _load_json_safe(path: str) -> Optional[Dict]:
|
||||
"""Lädt eine JSON-Datei; gibt None zurück wenn fehlend oder korrupt."""
|
||||
try:
|
||||
with open(path) as f:
|
||||
return json.load(f)
|
||||
except Exception as e:
|
||||
log.warning("JSON-Ladefehler %s: %s", path, e)
|
||||
return None
|
||||
|
||||
|
||||
def load_config() -> Dict[str, Any]:
|
||||
cfg = _defaults()
|
||||
# MQTT-Grundeinstellungen aus HAOS-Options (überschreibbar durch config.json)
|
||||
if os.path.exists(HA_OPTIONS_PATH):
|
||||
try:
|
||||
with open(HA_OPTIONS_PATH) as f:
|
||||
ha = json.load(f)
|
||||
ha = _load_json_safe(HA_OPTIONS_PATH) or {}
|
||||
for k in ("mqtt_broker", "mqtt_port", "mqtt_user", "mqtt_pass"):
|
||||
if k in ha:
|
||||
cfg[k] = ha[k]
|
||||
except Exception as e:
|
||||
log.warning("HA options Fehler: %s", e)
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
try:
|
||||
with open(CONFIG_PATH) as f:
|
||||
cfg.update(json.load(f))
|
||||
except Exception as e:
|
||||
log.warning("Config-Datei Fehler: %s", e)
|
||||
# Eigene persistente Config — Hauptdatei, dann Backup als Fallback
|
||||
loaded = _load_json_safe(CONFIG_PATH)
|
||||
if loaded is None and os.path.exists(CONFIG_PATH + ".bak"):
|
||||
log.warning("config.json korrupt — lade Backup")
|
||||
loaded = _load_json_safe(CONFIG_PATH + ".bak")
|
||||
if loaded:
|
||||
cfg.update(loaded)
|
||||
return cfg
|
||||
|
||||
|
||||
def save_config():
|
||||
data = {
|
||||
"mqtt_broker": State.mqtt_cfg.get("mqtt_broker", ""),
|
||||
@@ -150,8 +159,17 @@ def save_config():
|
||||
"surplus_devices": State.surplus_devices_cfg,
|
||||
"z2m_base": State.z2m_base,
|
||||
}
|
||||
with open(CONFIG_PATH, "w") as f:
|
||||
# Backup der letzten guten Config anlegen
|
||||
if os.path.exists(CONFIG_PATH):
|
||||
try:
|
||||
os.replace(CONFIG_PATH, CONFIG_PATH + ".bak")
|
||||
except OSError:
|
||||
pass
|
||||
# Atomarer Write: erst .tmp schreiben, dann umbenennen
|
||||
tmp = CONFIG_PATH + ".tmp"
|
||||
with open(tmp, "w") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
os.replace(tmp, CONFIG_PATH)
|
||||
|
||||
# ── Aggregation ───────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user