import type { PowerSample, QuarterHourStatus } from "../types.js"; export const BLOCK_DURATION_MS = 15 * 60 * 1000; export function blockStartFor(timestampMs: number): number { return Math.floor(timestampMs / BLOCK_DURATION_MS) * BLOCK_DURATION_MS; } /** * Computes the running and projected average power for the current 15-minute * billing block, given a step-function reading of power samples (each sample * holds until the next one arrives). Samples outside the current block are * ignored; if the block has no samples yet, power is assumed to be 0 so far. */ export function computeQuarterHourStatus( samples: PowerSample[], nowMs: number, targetW: number, ): QuarterHourStatus { const blockStartMs = blockStartFor(nowMs); const blockEndMs = blockStartMs + BLOCK_DURATION_MS; const relevant = samples .filter((s) => s.timestampMs >= blockStartMs && s.timestampMs <= nowMs) .sort((a, b) => a.timestampMs - b.timestampMs); let energyWms = 0; let cursorMs = blockStartMs; let cursorWatts = relevant.length > 0 ? relevant[0].watts : 0; for (const sample of relevant) { energyWms += cursorWatts * (sample.timestampMs - cursorMs); cursorMs = sample.timestampMs; cursorWatts = sample.watts; } energyWms += cursorWatts * (nowMs - cursorMs); const elapsedMs = nowMs - blockStartMs; const runningAverageW = elapsedMs > 0 ? energyWms / elapsedMs : 0; const remainingMs = blockEndMs - nowMs; const projectedEnergyWms = energyWms + cursorWatts * remainingMs; const projectedAverageW = projectedEnergyWms / BLOCK_DURATION_MS; return { blockStartMs, blockEndMs, elapsedFractionOfBlock: elapsedMs / BLOCK_DURATION_MS, runningAverageW, projectedAverageW, targetW, overTarget: projectedAverageW > targetW, }; }