fix: realtime + KPI power dedup by station prefix (v1.4.3)
Realtime endpoint was summing ALL device power readings, causing double-counting when multiple devices share the same Sungrow station. E.g. 10 devices × station-level power = 5x inflated total. Fix: GROUP BY station prefix (first 3 chars of device name) and take MAX per station. Same fix applied to KPI daily_generation. Result: 5,550 kW → 1,931 kW (matches iSolarCloud's 2,049 kW within the 15-min collection timing window). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,31 +35,32 @@ async def get_solar_kpis(db: AsyncSession = Depends(get_db), user: User = Depend
|
||||
"total_rated_kw": 0, "daily_generation_kwh": 0,
|
||||
}
|
||||
|
||||
# Get latest daily_energy per PV device for today
|
||||
# Get latest daily_energy per station (dedup by device name prefix)
|
||||
# Sungrow collectors report station-level data per device, so multiple
|
||||
# devices sharing the same station report identical values.
|
||||
# Group by station prefix (first 3 chars of name, e.g. "AP1" vs "AP2")
|
||||
# and take MAX per station to avoid double-counting.
|
||||
from sqlalchemy import text as sa_text
|
||||
daily_gen_q = await db.execute(
|
||||
select(
|
||||
EnergyData.device_id,
|
||||
func.substring(Device.name, 1, 3).label("station"),
|
||||
func.max(EnergyData.value).label("max_energy"),
|
||||
).select_from(EnergyData).join(
|
||||
Device, EnergyData.device_id == Device.id
|
||||
).where(
|
||||
and_(
|
||||
EnergyData.timestamp >= today_start,
|
||||
EnergyData.data_type == "daily_energy",
|
||||
EnergyData.device_id.in_(pv_ids),
|
||||
)
|
||||
).group_by(EnergyData.device_id)
|
||||
).group_by(sa_text("1"))
|
||||
)
|
||||
|
||||
# Check if values are station-level (all identical) or device-level
|
||||
daily_values = daily_gen_q.all()
|
||||
if not daily_values:
|
||||
daily_generation_kwh = 0
|
||||
else:
|
||||
values = [row[1] or 0 for row in daily_values]
|
||||
# If all values are identical, it's station-level data — use max (not sum)
|
||||
if len(set(values)) == 1 and len(values) > 1:
|
||||
daily_generation_kwh = values[0]
|
||||
else:
|
||||
daily_generation_kwh = sum(values)
|
||||
daily_generation_kwh = sum(row[1] or 0 for row in daily_values)
|
||||
|
||||
# Performance Ratio (PR) = actual output / (rated capacity * peak sun hours)
|
||||
# Approximate peak sun hours from time of day (simplified)
|
||||
|
||||
Reference in New Issue
Block a user