Feature: Abrechnungsperiode + Strompreise im Energie-Dashboard (v1.7.0)

- history.py: Tabelle period_starts — speichert kWh-Zählerstand zu Monats-/Jahresbeginn
- main.py: price_import/price_export in Config; /api/period-energy Endpoint
- Web UI: Preisfelder in Einstellungen (€/kWh Bezug + Vergütung)
- Energie-Dashboard: Cards zeigen Monat/Jahr kWh + Kosten statt All-Time-Total

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
retr0
2026-04-28 22:37:09 +02:00
parent 5972ef2c35
commit e9ca2fcc7d
4 changed files with 138 additions and 17 deletions
+41 -1
View File
@@ -2,7 +2,7 @@ import logging
import sqlite3
import threading
import time
from typing import Dict, List, Tuple
from typing import Dict, List, Optional, Tuple
log = logging.getLogger(__name__)
@@ -37,11 +37,51 @@ def init_db():
CREATE INDEX IF NOT EXISTS idx_inv_sensor_ts
ON measurements(inv_id, sensor_id, ts)
""")
# Periodenstarts: kWh-Zählerstand zu Beginn jeder Abrechungsperiode
c.execute("""
CREATE TABLE IF NOT EXISTS period_starts (
agg_id TEXT NOT NULL,
period_type TEXT NOT NULL,
period_key TEXT NOT NULL,
value REAL NOT NULL,
PRIMARY KEY (agg_id, period_type, period_key)
)
""")
c.commit()
cleanup_old()
log.info("History DB initialisiert: %s", DB_PATH)
def period_key(period_type: str) -> str:
import datetime
now = datetime.date.today()
return now.strftime("%Y-%m") if period_type == "monthly" else now.strftime("%Y")
def save_period_start_if_new(agg_id: str, period_type: str, key: str, current_value: float):
"""Speichert den Startwert nur wenn diese Periode noch nicht existiert."""
with _lock:
c = _get_conn()
c.execute("""
INSERT OR IGNORE INTO period_starts(agg_id, period_type, period_key, value)
VALUES (?, ?, ?, ?)
""", (agg_id, period_type, key, current_value))
c.commit()
def get_period_consumption(agg_id: str, period_type: str, key: str,
current_value: float) -> Optional[float]:
"""Verbrauch seit Periodenbeginn; None wenn noch kein Startwert gespeichert."""
with _lock:
row = _get_conn().execute("""
SELECT value FROM period_starts
WHERE agg_id=? AND period_type=? AND period_key=?
""", (agg_id, period_type, key)).fetchone()
if row is None:
return None
return max(0.0, current_value - row[0])
def write_batch(inv_id: str, ts: float, values: Dict[str, float]):
rows = [(inv_id, sid, ts, val) for sid, val in values.items()]
with _lock: