- JavaScript 99.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
CI / Frontend tests, backend tests, and build (push) Successful in 53s
|
||
| .forgejo/workflows | ||
| backend | ||
| docs | ||
| Specs | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| CHANGELOG.md | ||
| docker-compose.full.yml | ||
| docker-compose.yml | ||
| Dockerfile | ||
| index.html | ||
| nginx.conf | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.js | ||
| README.md | ||
| renovate.json | ||
| tailwind.config.js | ||
| vite.config.js | ||
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=oidcAUTH_REQUIRED=truePUBLIC_APP_URL=https://battery.example.comSESSION_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.
- Reports: saved report snapshots and shareable home dashboards.
- 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
Migrations are applied in filename order and recorded individually in the
schema_migrations table, so each file runs exactly once.
Current migrations:
| File | Description |
|---|---|
001_initial.sql |
Initial schema: users, homes, memberships, battery/inverter/solar/installer configurations, circuit assignments, imported usage, weather, external integrations, app state snapshots, background jobs |
002_billing_auth_ops.sql |
Billing periods, home auth columns, and supporting indexes |
003_circuit_whole_home_exclusion.sql |
Adds exclude_from_whole_home to circuit_assignments |
003_home_audit_and_current_configs.sql |
Home audit events plus name indexes on home-scoped configuration tables |
004_home_calculation_results.sql |
Cached calculation results per home |
005_calculation_cache_revisions.sql |
Home data revision tracking and calculation cache lookup indexes |
006_home_report_snapshots.sql |
Saved report snapshots |
Note: two migrations share the 003_ prefix. This is harmless — ordering is
deterministic and each is tracked by its full filename — but new migrations should
use the next unused number (007_) to keep the sequence unambiguous.
Development
Frontend tests:
npm test
Backend tests:
npm --prefix backend test
Production build:
npm run build
CI runs all three of the above on every pull request to main
(.forgejo/workflows/ci.yml).
Contributing
- Planned work lives in Forgejo issues and milestones,
not in a roadmap document.
docs/roadmap.mdis retained as a historical record of the completed build-out. - Changes are recorded in
CHANGELOG.mdfollowing Keep a Changelog. Add user-visible changes to theUnreleasedsection as you go. - Versioning follows Semantic Versioning. The project is
at
1.0.0as of 2026-06-03. - Dependencies are managed by Renovate (
renovate.json). Major updates require approval from the Renovate dependency dashboard rather than opening automatically.