Skip to main content
See Smart scheduler for the user-facing summary.
Trigger: Vercel cron 30 16 * * * UTC → /api/cron/scheduler (plus Re-optimise now from Planner). Steps (simplified):
  1. For each scheduler_enabled user, repair inverter drift if the live FoxESS schedule ≠ DB plan
  2. Load tomorrow’s Agile export rates for the user’s GSP
  3. Find peak export window above minExportRateP (findPeakExportWindow — highest revenue sub-window within contiguous eligible runs, length/start tiebreakers; limited by discharge capacity and max 4 hours)
  4. Skip export if live SoC < 30%
  5. Insert Force Charge periods for IO cheap window (GraphQL planned dispatches, or settings fallback 23:30–05:30 @ 7p)
  6. Fill remaining periods with Self-use
  7. Push tomorrowSchedule to FoxESS (full next calendar day)
  8. Log run + estimates to Neon (scheduler_log, is_current)
Manual re-optimise: preserve pending cron export when inverter matches plan (preserved); else push missed today export; else rebuild tomorrow. Manual override merge: mergeScheduleWithOverride() inserts a 30-minute period into today’s plan; export interrupt requires confirm (HTTP 409 without it). FoxESS errno 40257 on full-day merge may fall back to a short override that wipes the rest of the day on the inverter. Drift repair: repairInverterScheduleIfNeeded() on home load (≤1 check / 15 min), cron, and manual re-optimise; FoxESS scheduler/get cached 60s. Source of truth: lib/domain/scheduler.ts, lib/domain/scheduler-sync.ts, lib/providers/foxess-scheduler.ts.