xenofex7.github.io/solar-tracker
A small, locally-hosted web app that compares actual vs. target solar yield. Actuals come from Home Assistant (Long-Term Statistics via WebSocket), Fronius Solar.web (cloud API, Premium account), or manual entry. Targets are monthly kWh goals from the plant planning.
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # set HA_URL, HA_TOKEN, HA_ENTITY_ID
python seed_demo.py # optional: demo data so the charts render
python app.py # opens http://localhost:5000Solar-Tracker ships with a built-in account system (since v2.1) but stays
zero-config: a fresh install seeds a single admin user without a password
and auto-logs in just like the old behavior. As soon as you set a password
on admin (or add a second user) under Settings -> Users, the login
screen kicks in and auto-login is disabled.
Two roles are supported:
- admin - full access to the dashboard, settings, and write APIs.
- readonly - dashboard and
GET /api/*only; no settings, no writes.
Read-only users can also call the JSON API via HTTP Basic Auth, e.g.
curl -u viewer:pw https://solar.example/api/summary?year=2025.
Passwords are hashed with PBKDF2-HMAC-SHA256 (200k iterations) and the
session secret is read from FLASK_SECRET_KEY or auto-generated on first
run. Set SESSION_COOKIE_SECURE=true when serving over HTTPS.
If you expose the port to the internet, still put it behind a TLS-terminating reverse proxy (Caddy/nginx, Tailscale, or a VPN). Defense in depth.
If you forget the admin password, use the bundled CLI from the project root:
.venv/bin/python -m scripts.manage_users list
.venv/bin/python -m scripts.manage_users set-password admin # prompts
.venv/bin/python -m scripts.manage_users reset-admin # nuke + reseed passwordless admin (auto-login)
.venv/bin/python -m scripts.manage_users reset-admin --password hunter2Inside Docker, exec into the container:
docker compose exec solar-tracker python -m scripts.manage_users listMore recipes (backup, upgrade, telemetry, logs, factory reset) live in
docs/COOKBOOK.md.
The published image is hosted on GitHub Container Registry and
docker-compose.yml references it by default, so no source checkout
is required to deploy:
mkdir solar-tracker && cd solar-tracker
curl -O https://raw.githubusercontent.com/xenofex7/solar-tracker/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/xenofex7/solar-tracker/main/.env.example
mv .env.example .env # set HA_URL, HA_TOKEN, HA_ENTITY_ID
docker compose up -d # pulls ghcr.io/xenofex7/solar-tracker:latestThe SQLite database lives in ./data on the host (mounted into the
container), so stopping or recreating the container preserves all
data. The container runs gunicorn with two workers.
Available tags: latest, plus pinned major / minor / patch tags
(e.g. 1, 1.8, 1.8.0). See
ghcr.io/xenofex7/solar-tracker.
If you have the source checked out, docker compose build rebuilds
the image locally (the compose file keeps build: . as a fallback).
- 14 charts including monthly actual vs. target, deviation in %, cumulative yearly yield, daily production with 7-day rolling average, calendar heatmap, daily distribution per month (min/median/max), year-on-year comparison, top 5 days, specific yield (kWh/kWp) and day quality donut.
- Payback chart with cumulative revenue vs. investment and forecast.
- Energy and finance flow charts per billing period (import, export, self-consumption, savings vs. no PV).
- KPI tiles in three groups:
- Production: YTD actual / target, Δ absolute / %, best day, specific yield, days recorded.
- Finances: investment, revenue to date, progress, payback date.
- Self-consumption & grid: net cost, savings vs. no PV, effective electricity price, self-consumed, self-consumption rate.
- Home Assistant (Long-Term Statistics via WebSocket).
- Fronius Solar.web (cloud API, Premium account required).
- Manual daily entry on
/entry. - Quarterly grid bills (import + export) on
/settings.
- 5 languages (DE, EN, FR, IT, ES), light + dark mode.
- Dates as
dd.mm.yyyy, Swiss thousands (1'234 kWh). - "Today" marker on daily and cumulative charts (current year only).
- YTD target is pro-rated to today for the current year.
The app connects to HA_URL via WebSocket and calls
recorder/statistics_during_period with period: "day" and
types: ["change"]. Data therefore comes from Long-Term Statistics, which
Home Assistant keeps indefinitely - unlike the recorder history, which is
purged after purge_keep_days. This lets you back-fill and re-sync multiple
years at once.
Expected sensor: an energy sensor with device_class: energy and
state_class: total_increasing (or total), e.g. sensor.solar_total_energy.
The sync form on /settings defaults to the last six months. Each run
overwrites existing entries for the selected days - including manual ones -
so the database stays in sync with Home Assistant.
As an alternative to Home Assistant, Solar-Tracker can pull daily PV production directly from the Fronius Solar.web cloud API. This requires a Solar.web Premium subscription and an API access key pair, which you generate in your Solar.web account under API access.
Configure SOLARWEB_ACCESS_KEY_ID and SOLARWEB_ACCESS_KEY_VALUE in .env.
SOLARWEB_PV_SYSTEM_ID is optional - if exactly one PV system is linked to
the account it is auto-resolved on first use.
In Settings -> Datensynchronisation, pick "Fronius Solar.web" as the data source. The dashboard auto-sync and the manual sync button will then both pull from Solar.web instead of Home Assistant. Sources without credentials are listed but greyed out.
For manual or cron-based syncs:
.venv/bin/python -m scripts.sync_solarweb --days 30
.venv/bin/python -m scripts.sync_solarweb --from 2026-01-01 --to 2026-04-30
.venv/bin/python -m scripts.sync_solarweb --list-systemsThe HTTP endpoint is POST /api/sync/solarweb (admin-only).
An optional MCP server in mcp_server/ exposes
the full REST API to MCP-aware hosts (Claude Desktop, Claude Code, etc.)
as 26 tools and 4 guided prompts.
Typical flows:
- Drop an electricity invoice into the chat and have it parsed and
imported (
import_electricity_invoice). - Ask for a year-end report (
yearly_report). - Tell the model to find gaps in the production history and sync them
(
sync_missing_days). - Record an installer/inverter receipt (
import_investment_receipt).
Quick setup:
pip install -e ./mcp_server
# In the Solar-Tracker UI: Settings -> API tokens -> create tokenConfigure the MCP host (env vars SOLAR_TRACKER_URL,
SOLAR_TRACKER_TOKEN) and launch with the solar-tracker-mcp console
script. Full instructions and the tool list live in
mcp_server/README.md.
.env keys:
| Key | Purpose |
|---|---|
HA_URL |
Base URL of Home Assistant (e.g. http://ha.local:8123) |
HA_TOKEN |
Long-Lived Access Token |
HA_ENTITY_ID |
Statistic entity (e.g. sensor.solar_total_energy) |
SOLARWEB_ACCESS_KEY_ID |
Fronius Solar.web API access key ID (Premium) |
SOLARWEB_ACCESS_KEY_VALUE |
Fronius Solar.web API access key secret |
SOLARWEB_PV_SYSTEM_ID |
Optional; auto-resolved if account has exactly one system |
PLANT_KWP |
Installed peak power, used for specific yield (kWh/kWp) |
FLASK_HOST |
Bind address (default 127.0.0.1, set 0.0.0.0 in Docker) |
FLASK_PORT |
HTTP port (default 5000) |
FLASK_DEBUG |
true enables Flask debug mode |
TELEMETRY_ENABLED |
false opts out of anonymous instance telemetry |
The plant size can also be set on /settings, which overrides PLANT_KWP.
Solar-Tracker sends one anonymous heartbeat per day so I can see how many instances are running and which versions are in the wild. The payload is:
- a random
instance_id(UUID generated on first start, persisted indata/telemetry.json) - the app
version - the Python version
No user data, no IP, no Home Assistant values, no request tracking. Events are
sent to a privately-hosted Umami instance. To opt out, set
TELEMETRY_ENABLED=false in your .env.
Bug reports and feature requests are welcome on GitHub Issues.