Signed-off-by: Thomas Klaehn <tkl@blackfinn.de>
This commit is contained in:
2026-04-30 09:13:47 +02:00
parent 74f7266632
commit d4c08467cb
9 changed files with 857 additions and 26 deletions
+41 -16
View File
@@ -38,10 +38,11 @@ var periodDefaults = map[string]int{
}
// viewForRange picks the right continuous aggregate views for the given time range.
// Returns empty strings for invView when the range should use raw tables (see getPower).
func viewForRange(timeRange string) (invView, meterView, chargerView, interval string) {
switch timeRange {
case "1h":
return "inverter_10m", "power_meter_10m", "charger_10m", "1 hour"
return "", "", "", "1 hour" // raw tables, 30s buckets
case "6h":
return "inverter_10m", "power_meter_10m", "charger_10m", "6 hours"
case "7d":
@@ -90,18 +91,35 @@ func getPower(ctx context.Context, pool *pgxpool.Pool, timeRange string) ([]Powe
return m, rows.Err()
}
pvSQL := fmt.Sprintf(
`SELECT bucket, COALESCE(pv_power, 0) FROM %s WHERE bucket >= NOW() - '%s'::interval ORDER BY bucket`,
invView, interval)
houseSQL := fmt.Sprintf(
`SELECT bucket, COALESCE(total_power, 0) FROM %s WHERE device = 'house' AND bucket >= NOW() - '%s'::interval ORDER BY bucket`,
meterView, interval)
barnSQL := fmt.Sprintf(
`SELECT bucket, COALESCE(total_power, 0) FROM %s WHERE device = 'barn' AND bucket >= NOW() - '%s'::interval ORDER BY bucket`,
meterView, interval)
chargerSQL := fmt.Sprintf(
`SELECT bucket, COALESCE(power, 0) FROM %s WHERE bucket >= NOW() - '%s'::interval ORDER BY bucket`,
chargerView, interval)
var pvSQL, houseSQL, barnSQL, chargerSQL string
if invView == "" {
// Raw tables with 30-second buckets for short live range
pvSQL = fmt.Sprintf(
`SELECT time_bucket('30 seconds', time) AS bucket, COALESCE(AVG(pv1_power + pv2_power), 0) FROM inverter WHERE time >= NOW() - '%s'::interval GROUP BY bucket ORDER BY bucket`,
interval)
houseSQL = fmt.Sprintf(
`SELECT time_bucket('30 seconds', time) AS bucket, COALESCE(AVG(l1_power + l2_power + l3_power), 0) FROM power_meter WHERE device = 'house' AND time >= NOW() - '%s'::interval GROUP BY bucket ORDER BY bucket`,
interval)
barnSQL = fmt.Sprintf(
`SELECT time_bucket('30 seconds', time) AS bucket, COALESCE(AVG(l1_power + l2_power + l3_power), 0) FROM power_meter WHERE device = 'barn' AND time >= NOW() - '%s'::interval GROUP BY bucket ORDER BY bucket`,
interval)
chargerSQL = fmt.Sprintf(
`SELECT time_bucket('30 seconds', time) AS bucket, COALESCE(AVG(power), 0) FROM charger WHERE time >= NOW() - '%s'::interval GROUP BY bucket ORDER BY bucket`,
interval)
} else {
pvSQL = fmt.Sprintf(
`SELECT bucket, COALESCE(pv_power, 0) FROM %s WHERE bucket >= NOW() - '%s'::interval ORDER BY bucket`,
invView, interval)
houseSQL = fmt.Sprintf(
`SELECT bucket, COALESCE(total_power, 0) FROM %s WHERE device = 'house' AND bucket >= NOW() - '%s'::interval ORDER BY bucket`,
meterView, interval)
barnSQL = fmt.Sprintf(
`SELECT bucket, COALESCE(total_power, 0) FROM %s WHERE device = 'barn' AND bucket >= NOW() - '%s'::interval ORDER BY bucket`,
meterView, interval)
chargerSQL = fmt.Sprintf(
`SELECT bucket, COALESCE(power, 0) FROM %s WHERE bucket >= NOW() - '%s'::interval ORDER BY bucket`,
chargerView, interval)
}
type result struct {
m map[time.Time]float64
@@ -152,9 +170,16 @@ func getPower(ctx context.Context, pool *pgxpool.Pool, timeRange string) ([]Powe
func getBattery(ctx context.Context, pool *pgxpool.Pool, timeRange string) ([]BatteryPoint, error) {
invView, _, _, interval := viewForRange(timeRange)
sql := fmt.Sprintf(
`SELECT bucket, COALESCE(battery_soc, 0) FROM %s WHERE bucket >= NOW() - '%s'::interval ORDER BY bucket`,
invView, interval)
var sql string
if invView == "" {
sql = fmt.Sprintf(
`SELECT time_bucket('30 seconds', time) AS bucket, COALESCE(AVG(battery_soc), 0) FROM inverter WHERE time >= NOW() - '%s'::interval GROUP BY bucket ORDER BY bucket`,
interval)
} else {
sql = fmt.Sprintf(
`SELECT bucket, COALESCE(battery_soc, 0) FROM %s WHERE bucket >= NOW() - '%s'::interval ORDER BY bucket`,
invView, interval)
}
rows, err := pool.Query(ctx, sql)
if err != nil {