No description
  • JavaScript 99.9%
Find a file
Ryan b31aa8cd27 Add battery degradation and rate-escalation-adjusted payback
- Add degradationPct (2%/yr) to all batteries and quote battery defaults
- Add calculateAdjustedPaybackYears to payback.js: year-by-year loop
  compounds escalation (savings grow with rates) and degradation (battery
  capacity fades) to find the real break-even year
- Restructure projectionScenarioList to carry batterySavings and
  solarSavings per scenario so each can be degraded at its own rate
- buildProjectionData degrades battery/solar savings independently while
  leaving rate-plan-switch savings untouched
- calculationEngine uses adjusted payback for all paybackYears,
  fullSystemPaybackYears, solarPaybackYears, paybackVsAnytimeYears,
  and quote comparison payback; pulls rateIncreasePct and
  battery.degradationPct from state/modeledSystem
- calculationRuntime passes degradation rates to buildProjectionData
  for server-side projection snapshots
- ProjectionsTab passes degradation rates to chart, fixes annual cost
  table to show rising costs as battery effectiveness decays, and
  updates the disclaimer footnotes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 13:41:20 -05:00
backend Optimize simulation, fix JSONB params, and improve home save/load 2026-06-03 16:38:52 -05:00
docs Add per-circuit whole-home backup exclusion 2026-06-01 14:47:01 -05:00
Specs Reorganization 2026-05-08 22:29:11 -05:00
src Add battery degradation and rate-escalation-adjusted payback 2026-06-05 13:41:20 -05:00
.dockerignore Add v1 Ameren MO rate & battery storage calculator 2026-05-08 23:16:54 -05:00
.env.example Complete power modeling roadmap 2026-05-14 00:03:59 -05:00
docker-compose.full.yml Add shared home calculation runtime 2026-05-22 12:48:14 -05:00
docker-compose.yml Add v1 Ameren MO rate & battery storage calculator 2026-05-08 23:16:54 -05:00
Dockerfile Wire full-stack auth proxy 2026-05-21 14:22:18 -05:00
index.html Add v1 Ameren MO rate & battery storage calculator 2026-05-08 23:16:54 -05:00
nginx.conf Wire full-stack auth proxy 2026-05-21 14:22:18 -05:00
package.json Add shared home calculation runtime 2026-05-22 12:48:14 -05:00
postcss.config.js Add v1 Ameren MO rate & battery storage calculator 2026-05-08 23:16:54 -05:00
README.md Add per-circuit whole-home backup exclusion 2026-06-01 14:47:01 -05:00
tailwind.config.js Add v1 Ameren MO rate & battery storage calculator 2026-05-08 23:16:54 -05:00
vite.config.js Add v1 Ameren MO rate & battery storage calculator 2026-05-08 23:16:54 -05:00

Ameren MO Rate Plan & Battery Storage Calculator

Interactive web app for comparing Ameren Missouri residential rate plans, modeling battery/solar ROI, evaluating backup runtime, and reconciling modeled bills against actual Ameren bills.

Run Frontend Only

docker compose up --build

Then open http://localhost:3000.

Run Full Stack

cp .env.example .env
docker compose -f docker-compose.full.yml up --build

The full stack starts:

  • frontend on port 3000
  • backend API on port 4000
  • PostgreSQL with a persistent Docker volume

Homelab / Caddy

The app works behind a normal subdomain reverse proxy. Point Caddy at the frontend container if you only run the frontend, or at the backend/frontend ports separately if you split them.

One simple pattern is:

battery.example.com {
  reverse_proxy 10.1.1.14:3000
}

If you expose backend APIs through the same hostname, proxy /api/* and /auth/* to the backend and everything else to the frontend:

battery.example.com {
  handle /api/* {
    reverse_proxy 10.1.1.14:4000
  }
  handle /auth/* {
    reverse_proxy 10.1.1.14:4000
  }
  handle {
    reverse_proxy 10.1.1.14:3000
  }
}

Authentik / OIDC

Authentication is configured with environment variables. No Authentik groups are required; home roles are managed inside the app.

Required for OIDC mode:

  • AUTH_MODE=oidc
  • AUTH_REQUIRED=true
  • PUBLIC_APP_URL=https://battery.example.com
  • SESSION_SECRET=<long random value>
  • OIDC_ISSUER_URL=<Authentik issuer URL>
  • OIDC_CLIENT_ID=<client id>
  • OIDC_CLIENT_SECRET=<client secret>
  • OIDC_REDIRECT_URI=https://battery.example.com/auth/callback

When using docker-compose.full.yml, the frontend container proxies /api/* and /auth/* to the backend container. Use FRONTEND_PORT for the public frontend port and BACKEND_HOST_PORT if you need to expose the backend directly. The backend always listens on port 4000 inside the Compose network.

The backend validates OIDC issuer, audience, expiration, signature, and login nonce. API routes for home-scoped data require home membership once auth is enabled.

Data Sources

  • Emporia usage: CSV import is the active path. Backend sync jobs and integration settings exist for a future official or user-provided adapter.
  • Weather: CSV import or Open-Meteo historical daily temperature sync using the home latitude/longitude.
  • Ameren billing: manual entry or CSV import. Ameren Missouri customer billing API support is not assumed.

Configuration And Backups

  • Browser state persists in localStorage.
  • JSON export/import remains available in the My Data tab.
  • Backend snapshot migration is available in the My Data tab when the API is reachable.
  • PostgreSQL backup example:
docker compose -f docker-compose.full.yml exec postgres pg_dump -U battery battery_storage > battery_storage.sql

Restore example:

docker compose -f docker-compose.full.yml exec -T postgres psql -U battery battery_storage < battery_storage.sql

Features

  • Overview: rate comparison, dispatch impact, and rate reference.
  • My Data: Emporia CSV import, circuit backup status, outage priority, whole-home exclusions, persistence tools.
  • Batteries: configurable battery library and cost metrics.
  • Backup: reserve settings, outage scenarios, load shedding, SOC/load charts.
  • Weather: home location, geocoding, weather import/sync, temperature correlation.
  • Solar: panel library, installer costs, export/no-export settings, lifetime production.
  • Billing: manual/CSV bill import and modeled-vs-actual reconciliation.
  • Quotes: installer quote scenarios with included hardware and revision history.
  • Projections: 15-year savings and payback projections.
  • Admin: auth status, homes, memberships, integration status, and backend diagnostics.

Database Migrations

Migrations run automatically on backend startup. To apply a new migration manually:

docker compose -f docker-compose.full.yml exec backend node src/migrate.js

Current migrations:

File Description
001_initial.sql Initial schema: users, homes, memberships, battery/inverter/solar configurations, circuit assignments, usage, weather, billing, jobs, calculation results
002_billing_auth_ops.sql Billing periods, auth operations
003_circuit_whole_home_exclusion.sql Adds exclude_from_whole_home column to circuit_assignments

Development

Frontend tests:

npm test

Backend tests:

npm --prefix backend test

Production build:

npm run build