Files
HAMeter/hameter/cost_state.py
2026-03-06 12:25:27 -05:00

57 lines
1.5 KiB
Python

"""Persistent cost state management.
Cost state is stored at /data/cost_state.json, separate from config.json.
This file is updated each time a cost calculation occurs, enabling
persistence across restarts without bloating the config file.
"""
import json
import logging
import os
import tempfile
from typing import Optional
logger = logging.getLogger(__name__)
COST_STATE_PATH = "/data/cost_state.json"
def load_cost_state(path: Optional[str] = None) -> dict:
"""Load persisted cost state from disk.
Returns:
Dict keyed by meter_id (str) with cost state values.
"""
path = path or COST_STATE_PATH
if not os.path.isfile(path):
return {}
try:
with open(path) as f:
return json.load(f)
except Exception as e:
logger.warning("Failed to load cost state: %s", e)
return {}
def save_cost_state(states: dict, path: Optional[str] = None):
"""Atomically save cost state to disk."""
path = path or COST_STATE_PATH
dir_path = os.path.dirname(path)
if dir_path:
os.makedirs(dir_path, exist_ok=True)
fd, tmp_path = tempfile.mkstemp(dir=dir_path or ".", suffix=".tmp")
try:
with os.fdopen(fd, "w") as f:
json.dump(states, f, indent=2)
f.write("\n")
f.flush()
os.fsync(f.fileno())
os.replace(tmp_path, path)
except Exception:
try:
os.unlink(tmp_path)
except OSError:
pass
raise