Skip to content

About

Web platform for real-time solar inverter telemetry tracking, data analytics, and performance monitoring. Powered by Python/Django, TimescaleDB, Redis, Django Channels, and React. Handles automated Modbus polling, live metrics via WebSockets, and alerts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Solar Inverters Monitoring

A self-hosted solar inverter monitoring system. Polls inverters over the Solarman Modbus RTU protocol, stores time-series data in TimescaleDB, and streams live updates to a React dashboard over WebSockets.

Built for personal use across a small fleet of inverters (Deye and DAH Solar), with register maps defined per-model so new inverter types can be added without touching backend code.

Screenshots

Deye SUN-6K-SG05LP1-EU

DAH Solar DHN-LVEH3P12K-EU-G1

Deye SUN-5K-G05P1-EU-AM2

Features

  • Polls multiple inverters in parallel via a management command
  • Register mapping is fully config-driven (YAML) — add a new inverter model without touching backend code
  • Live dashboard over WebSockets (Django Channels) plus REST endpoints for history and stats
  • Time-series storage on TimescaleDB (Postgres) with hypertables and compression
  • JWT authentication (djangorestframework-simplejwt)
  • Per-inverter daily/weekly energy stats (production, consumption, import, export)

Stack

Backend: Django 6, Django REST Framework, Django Channels + Daphne (ASGI), Redis, TimescaleDB (Postgres), pysolarmanv5

Frontend: React 19, Vite, Tailwind CSS, Recharts, React Router, Axios

Infra: Docker Compose (db, redis, backend, poller, frontend)

Project structure

.
├── backend/
│   ├── core/                  # Django project settings, ASGI/WSGI entrypoints
│   ├── monitoring/
│   │   ├── configs/           # YAML register maps, one per inverter model
│   │   ├── inverter_reader.py # Generic Modbus reader driven by YAML config
│   │   ├── management/commands/run_polling.py  # Polling loop (parallel, threaded)
│   │   ├── models.py          # Inverter, InverterData, EnergyStats
│   │   ├── consumers.py       # WebSocket consumer
│   │   └── views.py / urls.py / serializers.py
│   ├── manage.py
│   ├── requirements.txt
│   └── .env                   # not committed — see Environment variables
├── frontend/                  # React + Vite dashboard
├── backend.Dockerfile
├── frontend.Dockerfile
└── docker-compose.yml         # db, redis, backend, poller, frontend

Adding a new inverter

Register maps live in backend/monitoring/configs/*.yaml. Each file defines:

  • register_blocks — which Modbus register ranges to poll
  • registers — named metrics mapped to an address, type (uint16/int16/uint32/int32), and scale
  • pv_strings — per-string voltage/current addresses
  • pv_total_power_sources / grid_total_power_sources — registers to sum for aggregate values
  • database_mapping — which parsed metrics get written to InverterData model fields

To add a new inverter model, copy an existing config, adjust the register addresses for your model, and create an Inverter row in the admin pointing config_name at the new file (without the .yaml extension). No code changes required unless the model needs a metric the reader doesn't already support.

Register maps in this repo were reverse-engineered by hand against live hardware (no official Modbus documentation was available for these models), so they may not be 100% complete or accurate for every firmware revision.

Setup

Prerequisites

  • Docker and Docker Compose

Configure

cp backend/.env.example backend/.env   # fill in secrets, DB credentials, etc.

Run

docker compose up --build

This starts, in order:

  • db — TimescaleDB, on localhost:5433
  • redis — on localhost:6379
  • backend — runs migrations, then serves the API over Daphne (ASGI) on localhost:8001
  • poller — runs manage.py run_polling in a loop, polling all active inverters
  • frontend — Vite dev server on localhost:5173, proxying /api and /ws to backend

Open http://localhost:5173 for the dashboard.

First-time setup

Create a superuser and register your inverters through the Django admin:

docker compose exec backend python manage.py createsuperuser

Then visit http://localhost:8001/admin/ and add an Inverter entry per device (IP, port, Modbus ID, and config_name pointing at one of the YAML files in backend/monitoring/configs/).

Logs

docker compose logs -f backend
docker compose logs -f poller

API

Endpoint Description
POST /api/token/ Obtain JWT access/refresh token pair
POST /api/token/refresh/ Refresh access token
GET /api/inverters/ List inverters
GET /api/inverters/{id}/realtime/ Latest reading for an inverter
GET /api/inverters/{id}/history/?hours=N Historical readings for the last N hours
GET /api/inverters/{id}/stats/?days=N Daily energy stats for the last N days
WS /ws/inverter/{id}/ Live push of new readings

Environment variables

See backend/.env.example. At minimum you'll need:

  • DJANGO_SECRET_KEY, DJANGO_DEBUG, DJANGO_ALLOWED_HOSTS
  • DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT
  • REDIS_HOST, REDIS_PORT
  • CORS_ALLOWED_ORIGINS

docker-compose.yml overrides DB_HOST/REDIS_HOST to the in-network service names (db, redis) automatically — the values in .env are only used for running services outside Docker.

License

MIT — see LICENSE.

Disclaimer

The Solarman Modbus register maps used here were derived experimentally and are not affiliated with or endorsed by Deye, DAH Solar, or Solarman. Use at your own risk, particularly if writing to holding registers.

About

Web platform for real-time solar inverter telemetry tracking, data analytics, and performance monitoring. Powered by Python/Django, TimescaleDB, Redis, Django Channels, and React. Handles automated Modbus polling, live metrics via WebSockets, and alerts.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages